class EditProjectForm(FlaskForm): name = StringField(_("Project name"), validators=[DataRequired()]) password = StringField(_("Private code"), validators=[DataRequired()]) contact_email = StringField(_("Email"), validators=[DataRequired(), Email()]) project_history = BooleanField(_("Enable project history")) ip_recording = BooleanField(_("Use IP tracking for project history")) currency_helper = CurrencyConverter() default_currency = SelectField(_("Default Currency"), validators=[DataRequired()]) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.default_currency.choices = [ (currency_name, render_localized_currency(currency_name, detailed=True)) for currency_name in self.currency_helper.get_currencies() ] @property def logging_preference(self): """Get the LoggingMode object corresponding to current form data.""" if not self.project_history.data: return LoggingMode.DISABLED else: if self.ip_recording.data: return LoggingMode.RECORD_IP else: return LoggingMode.ENABLED def save(self): """Create a new project with the information given by this form. Returns the created instance """ project = Project( name=self.name.data, id=self.id.data, password=generate_password_hash(self.password.data), contact_email=self.contact_email.data, logging_preference=self.logging_preference, default_currency=self.default_currency.data, ) return project def update(self, project): """Update the project with the information from the form""" project.name = self.name.data # Only update password if changed to prevent spurious log entries if not check_password_hash(project.password, self.password.data): project.password = generate_password_hash(self.password.data) project.contact_email = self.contact_email.data project.logging_preference = self.logging_preference project.default_currency = self.default_currency.data return project
def create_app( configuration=None, instance_path="/etc/ihatemoney", instance_relative_config=True ): app = Flask( __name__, instance_path=instance_path, instance_relative_config=instance_relative_config, ) # If a configuration object is passed, use it. Otherwise try to find one. load_configuration(app, configuration) app.wsgi_app = PrefixedWSGI(app) # Get client's real IP # Note(0livd): When running in a non-proxy setup, is vulnerable to requests # with a forged X-FORWARDED-FOR header app.wsgi_app = ProxyFix(app.wsgi_app) validate_configuration(app) app.register_blueprint(web_interface) app.register_blueprint(apiv1) app.register_error_handler(404, page_not_found) # Configure the a, root="main"pplication setup_database(app) # Setup Currency Cache CurrencyConverter() mail = Mail() mail.init_app(app) app.mail = mail # Jinja filters app.jinja_env.globals["static_include"] = static_include app.jinja_env.globals["locale_from_iso"] = locale_from_iso app.jinja_env.filters["minimal_round"] = minimal_round # Translations babel = Babel(app) @babel.localeselector def get_locale(): # get the lang from the session if defined, fallback on the browser "accept # languages" header. lang = session.get( "lang", request.accept_languages.best_match(app.config["SUPPORTED_LANGUAGES"]), ) setattr(g, "lang", lang) return lang return app
def edit_project(): edit_form = EditProjectForm() import_form = UploadForm() # Import form if import_form.validate_on_submit(): try: import_project(import_form.file.data.stream, g.project) flash(_("Project successfully uploaded")) return redirect(url_for("main.list_bills")) except ValueError: flash(_("Invalid JSON"), category="danger") # Edit form if edit_form.validate_on_submit(): project = edit_form.update(g.project) # Update converted currency if project.default_currency != CurrencyConverter.no_currency: for bill in project.get_bills(): if bill.original_currency == CurrencyConverter.no_currency: bill.original_currency = project.default_currency bill.converted_amount = CurrencyConverter().exchange_currency( bill.amount, bill.original_currency, project.default_currency) db.session.add(bill) db.session.add(project) db.session.commit() return redirect(url_for("main.list_bills")) else: edit_form.name.data = g.project.name if g.project.logging_preference != LoggingMode.DISABLED: edit_form.project_history.data = True if g.project.logging_preference == LoggingMode.RECORD_IP: edit_form.ip_recording.data = True edit_form.contact_email.data = g.project.contact_email edit_form.default_currency.data = g.project.default_currency return render_template( "edit_project.html", edit_form=edit_form, import_form=import_form, current_view="edit_project", )
class TestCurrencyConverter(unittest.TestCase): converter = CurrencyConverter() mock_data = {"USD": 1, "EUR": 0.8115} converter.get_rates = MagicMock(return_value=mock_data) def test_only_one_instance(self): one = id(CurrencyConverter()) two = id(CurrencyConverter()) self.assertEqual(one, two) def test_get_currencies(self): self.assertCountEqual(self.converter.get_currencies(), ["USD", "EUR"]) def test_exchange_currency(self): result = self.converter.exchange_currency(100, "USD", "EUR") self.assertEqual(result, 81.15)
def create_app(configuration=None, instance_path="/etc/ihatemoney", instance_relative_config=True): app = Flask( __name__, instance_path=instance_path, instance_relative_config=instance_relative_config, ) # If a configuration object is passed, use it. Otherwise try to find one. load_configuration(app, configuration) app.wsgi_app = PrefixedWSGI(app) # Get client's real IP # Note(0livd): When running in a non-proxy setup, is vulnerable to requests # with a forged X-FORWARDED-FOR header app.wsgi_app = ProxyFix(app.wsgi_app) validate_configuration(app) app.register_blueprint(web_interface) app.register_blueprint(apiv1) app.register_error_handler(404, page_not_found) # Configure the a, root="main"pplication setup_database(app) # Setup Currency Cache CurrencyConverter() mail = Mail() mail.init_app(app) app.mail = mail # Jinja filters app.jinja_env.globals["static_include"] = static_include app.jinja_env.globals["locale_from_iso"] = locale_from_iso app.jinja_env.filters["minimal_round"] = minimal_round # Translations babel = Babel(app) # Undocumented currencyformat filter from flask_babel is forwarding to Babel format_currency # We overwrite it to remove the currency sign ¤ when there is no currency def currencyformat_nc(number, currency, *args, **kwargs): """ Same as flask_babel.Babel.currencyformat, but without the "no currency ¤" sign when there is no currency. """ return format_currency( number, currency if currency != CurrencyConverter.no_currency else "", *args, **kwargs).strip() app.jinja_env.filters["currencyformat_nc"] = currencyformat_nc @babel.localeselector def get_locale(): # get the lang from the session if defined, fallback on the browser "accept # languages" header. lang = session.get( "lang", request.accept_languages.best_match( app.config["SUPPORTED_LANGUAGES"]), ) setattr(g, "lang", lang) return lang return app
class BillForm(FlaskForm): date = DateField(_("Date"), validators=[DataRequired()], default=datetime.now) what = StringField(_("What?"), validators=[DataRequired()]) payer = SelectField(_("Payer"), validators=[DataRequired()], coerce=int) amount = CalculatorStringField(_("Amount paid"), validators=[DataRequired()]) currency_helper = CurrencyConverter() original_currency = SelectField(_("Currency"), validators=[DataRequired()]) external_link = URLField( _("External link"), validators=[Optional()], description=_("A link to an external document, related to this bill"), ) payed_for = SelectMultipleField(_("For whom?"), validators=[DataRequired()], coerce=int) submit = SubmitField(_("Submit")) submit2 = SubmitField(_("Submit and add a new one")) def save(self, bill, project): bill.payer_id = self.payer.data bill.amount = self.amount.data bill.what = self.what.data bill.external_link = self.external_link.data bill.date = self.date.data bill.owers = [ Person.query.get(ower, project) for ower in self.payed_for.data ] bill.original_currency = self.original_currency.data bill.converted_amount = self.currency_helper.exchange_currency( bill.amount, bill.original_currency, project.default_currency) return bill def fake_form(self, bill, project): bill.payer_id = self.payer bill.amount = self.amount bill.what = self.what bill.external_link = "" bill.date = self.date bill.owers = [ Person.query.get(ower, project) for ower in self.payed_for ] bill.original_currency = CurrencyConverter.no_currency bill.converted_amount = self.currency_helper.exchange_currency( bill.amount, bill.original_currency, project.default_currency) return bill def fill(self, bill, project): self.payer.data = bill.payer_id self.amount.data = bill.amount self.what.data = bill.what self.external_link.data = bill.external_link self.original_currency.data = bill.original_currency self.date.data = bill.date self.payed_for.data = [int(ower.id) for ower in bill.owers] self.original_currency.label = Label("original_currency", _("Currency")) self.original_currency.description = _( "Project default: %(currency)s", currency=render_localized_currency(project.default_currency, detailed=False), ) def set_default(self): self.payed_for.data = self.payed_for.default def validate_amount(self, field): if field.data == 0: raise ValidationError(_("Bills can't be null"))
def test_only_one_instance(self): one = id(CurrencyConverter()) two = id(CurrencyConverter()) self.assertEqual(one, two)