def _item_actions(self, item, readonly=False): actions = [] if item.private: actions.append( ItemAction( name="set_private", label=_("Make this issue public"), icon="glyphicon glyphicon-lock", url=self._request.route_url("getitfixed_set_public", id=getattr( item, self._id_field)), method="POST", )) else: actions.append( ItemAction( name="set_private", label=_("Make this issue private"), icon="glyphicon glyphicon-lock", url=self._request.route_url("getitfixed_set_private", id=getattr( item, self._id_field)), method="POST", )) return actions
class IssueViews(AbstractViews): _model = Issue _base_schema = base_schema _id_field = "hash" _author = USER_REPORTER _event_schema = event_schema MSG_COL = { "submit_ok": _( "Thank you for your report, " "it has been registered with following details, " "and will be treated as soon as possible." ), "copy_ok": _("Please check that the copy fits before submitting."), } @view_config( route_name="c2cgeoform_item", request_method="GET", renderer="getitfixed:templates/admin/issues/edit.jinja2", ) def edit(self): if self._is_new(): return HTTPNotFound() else: # Create a readonly issue form resp = super().edit(readonly=True) issue = self._get_object() event = Event(issue_id=issue.id) event.status = issue.status event.author = self._author event_form = Form( self._event_schema, formid="new_event_form", buttons=[Button(name="formsubmit", title=_("Submit"))], action=self._request.route_url( "c2cgeoform_item", table="events", id="new" ), ) resp.update( { "event_form": event_form, "event_form_render_args": (event_form.schema.dictify(event),), "event_form_render_kwargs": { "request": self._request, "user_admin": USER_ADMIN, "obj": issue, }, "events": self.events(issue), "wms_layer": self._get_object().type.wms_layer, } ) return resp @staticmethod def events(issue): return issue.public_events
class Type(Base): __tablename__ = "type" __table_args__ = {"schema": schema} __colanderalchemy_config__ = {"title": _("Type"), "plural": _("Types")} id = Column( Integer, primary_key=True, info={"colanderalchemy": {"title": _("Identifier"), "widget": HiddenWidget()}}, ) label_fr = Column( String(50), info={ "colanderalchemy": {"title": _("Label(fr)"), "widget": TextInputWidget()} }, ) label_en = Column( String(50), info={ "colanderalchemy": {"title": _("Label(en)"), "widget": TextInputWidget()} }, ) category_id = Column( Integer, ForeignKey("{}.category.id".format(schema)), info={ "colanderalchemy": { "title": _("Category"), "widget": RelationSelectWidget( Category, "id", "label_fr", order_by="label_fr", default_value=("", _("- Select -")), ), } }, ) category = relationship(Category, backref="types") wms_layer = Column( String(255), info={ "colanderalchemy": { "title": _("WMS layer"), "description": _( "Example: https://service.wms/?service=WMS&version=1.0.3&layer=layername" ), "widget": TextInputWidget(), } }, ) def icon_url(self, request): return self.category.icon_url(request) def label(self, locale): return getattr(self, "label_{}".format(locale))
def edit(self): if self._is_new(): return HTTPNotFound() else: # Create a readonly issue form resp = super().edit(readonly=True) issue = self._get_object() event = Event(issue_id=issue.id) event.status = issue.status event.author = self._author event_form = Form( self._event_schema, formid="new_event_form", buttons=[Button(name="formsubmit", title=_("Submit"))], action=self._request.route_url( "c2cgeoform_item", table="events", id="new" ), ) resp.update( { "event_form": event_form, "event_form_render_args": (event_form.schema.dictify(event),), "event_form_render_kwargs": { "request": self._request, "user_admin": USER_ADMIN, "obj": issue, }, "events": self.events(issue), "wms_layer": self._get_object().type.wms_layer, } ) return resp
def save(self): resp = super().save() if self._is_new(): if isinstance(resp, HTTPFound): # Send email to the issue Reporter self.send_notification_email( self._obj.email, "new_issue_email", self._request.route_url( "c2cgeoform_item", application="getitfixed_private", id=self._obj.hash, ), ) # Send email to the category Manager self.send_notification_email( self._obj.category.email, "admin_new_issue_email", self._request.route_url( "c2cgeoform_item", application="getitfixed_admin", id=self._obj.hash, ), ) return HTTPFound( self._request.route_url( "c2cgeoform_item", application="getitfixed_private", id=self._obj.hash, _query=[("msg_col", "submit_ok")], )) resp.update({"item_name": _("New"), "new": self._is_new()}) return resp
class Event(Base): __tablename__ = "event" __table_args__ = {"schema": schema} __colanderalchemy_config__ = {"title": _("Event"), "plural": _("Events")} id = Column( Integer, primary_key=True, info={"colanderalchemy": {"title": _("Identifier"), "widget": HiddenWidget()}}, ) issue_id = Column( Integer, ForeignKey("{}.issue.id".format(schema)), nullable=False, info={"colanderalchemy": {"title": _("Type"), "widget": HiddenWidget()}}, ) status = Column( Enum(*tuple(STATUSES.keys()), native_enum=False, name="status"), nullable=False, info={ "colanderalchemy": { "title": _("Status"), "widget": SelectWidget( values=list(STATUSES.items()), item_css_class="item-status" ), } }, ) date = Column( DateTime(timezone=True), nullable=False, server_default=func.now(), info={"colanderalchemy": {"title": _("Date"), "widget": HiddenWidget()}}, ) comment = Column( Text, info={"colanderalchemy": {"title": _("Comment"), "missing": ""}} ) private = Column( Boolean, info={ "colanderalchemy": { "title": _("Private"), "widget": CheckboxWidget(item_css_class="item-private"), } }, ) author = Column( Enum(*tuple(USER_AUTHORS.keys()), native_enum=False, name="author"), nullable=False, default="new", info={"colanderalchemy": {"title": _("Author"), "widget": HiddenWidget()}}, )
def send_email(request, to, template_name, template_args=[], template_kwargs={}): """Send email based on different templates Templates are set in the vars.yaml file """ settings = request.registry.settings smtp = settings["smtp"] if not smtp: LOG.warning("Email cannot be sent as smtp service is not configured.") return False template = settings["getitfixed"][template_name] sender = template["email_from"] body = _(template["email_body"]).format(template, *template_args, **template_kwargs) msg = MIMEText(body, _charset="UTF-8") msg["From"] = Header(sender) msg["To"] = Header(to) # msg['Bcc'] = Header(sender) msg["Subject"] = Header(_(template["email_subject"]), "utf-8") # Connect to server smtp_host = smtp["host"] if "ssl" in smtp and smtp["ssl"]: server = smtplib.SMTP_SSL(smtp_host) else: server = smtplib.SMTP(smtp_host) if "starttls" in smtp and smtp["starttls"]: server.starttls() if "user" in smtp and smtp["user"]: server.login(smtp["user"], smtp["password"]) # Send message server.sendmail(sender, [to, sender], msg.as_string()) # Diconnect from server server.quit() return True
def edit(self): if self._is_new(): base_edit = super().edit() base_edit["form_render_kwargs"].update( {"deps": get_types(self._request)}) base_edit["item_name"] = _("New") base_edit["new"] = True return base_edit else: if not self._request.matchdict["id"].isdigit(): raise HTTPNotFound() base_edit = super().edit(schema=follow_schema, readonly=True) obj = self._get_object() base_edit["item_name"] = obj.description base_edit["new"] = False base_edit["wms_layer"] = obj.type.wms_layer return base_edit
class Photo(FileData, Base): __tablename__ = "photo" __table_args__ = {"schema": schema} # Setting unknown to 'preserve' is required in classes used as a # FileUpload field. __colanderalchemy_config__ = { "title": _("Photo"), "unknown": "preserve", "missing": colander.required, "widget": deform_ext.FileUploadWidget( id_field="hash", get_url=lambda request, id_: request.route_url( "c2cgeoform_item", table="photos", id=id_ ), ), } hash = Column( Text, nullable=False, unique=True, default=lambda: str(uuid4()), info={"c2cgeoform": {"duplicate": False}}, ) issue_id = Column(Integer, ForeignKey("{}.issue.id".format(schema)))
class Category(Base): __tablename__ = "category" __table_args__ = {"schema": schema} __colanderalchemy_config__ = {"title": _("Category"), "plural": _("Categories")} id = Column( Integer, primary_key=True, info={"colanderalchemy": {"title": _("Identifier"), "widget": HiddenWidget()}}, ) label_fr = Column( String(50), info={ "colanderalchemy": {"title": _("Label(fr)"), "widget": TextInputWidget()} }, ) label_en = Column( String(50), info={ "colanderalchemy": {"title": _("Label(en)"), "widget": TextInputWidget()} }, ) email = Column( String(254), nullable=False, info={"colanderalchemy": {"title": _("Email"), "widget": TextInputWidget()}}, ) icon = Column( String(150), info={"colanderalchemy": {"title": _("Label(en)"), "widget": HiddenWidget()}}, ) def icon_url(self, request): return generate_url(request, self.icon or default_icon()) def label(self, locale): return getattr(self, "label_{}".format(locale))
class Issue(Base): __tablename__ = "issue" __table_args__ = {"schema": schema} __colanderalchemy_config__ = { "title": _("Issue"), "plural": _("Issues"), "widget": FormWidget(fields_template="issue_fields"), } id = Column( Integer, primary_key=True, info={ # the `colanderalchemy` property allows to set a custom title for the # column or to use a specific widget. "colanderalchemy": {"title": _("Identifier"), "widget": HiddenWidget()} }, ) hash = Column( Text, nullable=False, unique=True, default=lambda: str(uuid4()), info={"colanderalchemy": {"exclude": True}, "c2cgeoform": {"duplicate": False}}, ) request_date = Column( Date, nullable=False, server_default=func.now(), info={"colanderalchemy": {"title": _("Request date")}}, ) geometry = Column( geoalchemy2.Geometry("POINT", 4326, management=True), info={ "colanderalchemy": { "title": _("Position"), "typ": colander_ext.Geometry( "POINT", srid=4326, map_srid=_map_config["srid"] ), "widget": deform_ext.MapWidget( map_options=_map_config, item_css_class="item-geometry" ), } }, ) type_id = Column( Integer, ForeignKey("{}.type.id".format(schema)), nullable=False, info={ "colanderalchemy": { "title": _("Type"), "widget": RelationSelectWidget( Type, "id", "label_en", order_by="label_en", default_value=("", _("- Select -")), ), } }, ) type = relationship(Type, info={"colanderalchemy": {"exclude": True}}) status = Column( Enum(*tuple(STATUSES.keys()), native_enum=False, name="status"), nullable=False, default="new", info={ "colanderalchemy": { "title": _("Status"), "widget": SelectWidget( values=list(STATUSES.items()), readonly=True, item_css_class="item-status", ), } }, ) @property def status_de(self): return self.status_i18n("de") @property def status_en(self): return self.status_i18n("en") @property def status_fr(self): return self.status_i18n("fr") def status_i18n(self, locale): localizer = make_localizer(locale, [resource_filename("getitfixed", "locale")]) return localizer.translate(STATUSES[self.status]) description = Column( Text, nullable=False, info={ "colanderalchemy": { "title": _("Description of the problem"), "widget": TextAreaWidget(rows=3), } }, ) localisation = Column( String(254), info={"colanderalchemy": {"title": _("Localisation")}} ) photos = relationship( Photo, cascade="all, delete-orphan", info={"colanderalchemy": {"title": _("Photos")}}, ) firstname = Column(String(100), info={"colanderalchemy": {"title": _("Firstname")}}) lastname = Column(String(100), info={"colanderalchemy": {"title": _("Lastname")}}) phone = Column( String(20), info={"colanderalchemy": {"title": _("Phone"), "widget": TelWidget()}}, ) email = Column( String(254), nullable=False, info={ "colanderalchemy": { "title": _("Email"), "validator": colander.Email(), "description": _( "This field is required to keep you informed about issue events" ), } }, ) private = Column( Boolean, nullable=False, server_default=text("False"), info={"colanderalchemy": {"title": _("Private"), "exclude": True}}, ) events = relationship( "Event", order_by="desc(Event.date)", backref=backref("issue", info={"colanderalchemy": {"exclude": True}}), info={ "colanderalchemy": { "title": _("Events"), "widget": SequenceWidget(readonly=True, item_css_class="item-events"), } }, ) public_events = relationship( "Event", order_by="desc(Event.date)", primaryjoin="and_(Event.issue_id==Issue.id, Event.private==False)", ) category = association_proxy("type", "category") def icon_url(self, request): return self.type.icon_url(request) if self.type else default_icon_url(request) def full_name(self): return "{} {}".format(self.firstname or "", self.lastname or "").strip()
_getitfixed_config = (config.get_config() or {}).get("getitfixed", {}) _map_config = { **default_map_settings, **{"mobile": True}, **_getitfixed_config.get("map", {}), } STATUS_NEW = "new" STATUS_VALIDATED = "validated" STATUS_IN_PROGRESS = "in_progress" STATUS_REPORTER = "waiting_for_reporter" STATUS_RESOLVED = "resolved" STATUSES = { STATUS_NEW: _("New"), STATUS_VALIDATED: _("Validated"), STATUS_IN_PROGRESS: _("In progress"), STATUS_REPORTER: _("Waiting for Reporter"), STATUS_RESOLVED: _("Resolved"), } USER_ADMIN = "admin" USER_REPORTER = "customer" USER_AUTHORS = {USER_REPORTER: _("Reporter"), USER_ADMIN: _("Administrator")} def default_icon(): return _getitfixed_config.get( "default_icon", "static://getitfixed:static/icons/cat-default.png" )
def duplicate(self): base_duplicate = super().duplicate() base_duplicate["form_render_kwargs"].update( {"deps": get_types(self._request)}) base_duplicate["item_name"] = _("New") return base_duplicate
from getitfixed.models.getitfixed import Category, Issue, Type, STATUS_NEW from getitfixed.emails.email_service import send_email from getitfixed.i18n import _ _list_field = partial(ListField, Issue) new_schema = GeoFormSchemaNode(Issue, excludes=["request_date", "events", "status"]) new_schema.add_before( "type_id", SchemaNode( Int(), name="category_id", title=_("Category"), widget=RelationSelectWidget(Category, "id", "label_fr", order_by="label_fr"), ), ) follow_schema = GeoFormSchemaNode( Issue, includes=[ "status", "request_date", "type_id", "description", "localisation",