Exemple #1
0
 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
Exemple #2
0
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
Exemple #3
0
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))
Exemple #4
0
    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
Exemple #5
0
    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
Exemple #6
0
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()}},
    )
Exemple #7
0
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
Exemple #8
0
 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
Exemple #9
0
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)))
Exemple #10
0
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))
Exemple #11
0
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()
Exemple #12
0
_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"
    )
Exemple #13
0
 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
Exemple #14
0
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",