def sync_sources(db_session: SessionLocal, project: Project): """Syncs sources from external sources.""" plugin = plugin_service.get_active_instance( db_session=db_session, plugin_type="source", project_id=project.id ) if not plugin: log.debug(f"No active plugins were found. PluginType: 'source' ProjectId: {project.id}") return log.debug(f"Getting source information via: {plugin.plugin.slug}") sources = source_service.get_all(db_session=db_session, project_id=project.id) for s in sources: log.debug(f"Syncing Source. Source: {s}") if not s.external_id: log.debug(f"Skipping source, no externalId Source: {s}") continue data = plugin.instance.get(external_id=s.external_id) if data: for k, v in data.items(): setattr(s, k, v) db_session.commit()
def remove_participant(user_email: str, incident: Incident, db_session: SessionLocal): """Removes a participant.""" inactivated = inactivate_participant(user_email, incident, db_session) if inactivated: participant = get_by_incident_id_and_email(db_session=db_session, incident_id=incident.id, email=user_email) log.debug( f"Removing {participant.individual.name} from {incident.name} incident..." ) participant.service = None db_session.add(participant) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description=f"{participant.individual.name} has been removed", incident_id=incident.id, )
def add_participant( user_email: str, incident: Incident, db_session: SessionLocal, service_id: int = None, role: ParticipantRoleType = ParticipantRoleType.participant, ): """Adds a participant.""" # we get or create a new individual individual = individual_service.get_or_create(db_session=db_session, incident=incident, email=user_email) # we get or create a new participant participant_role = ParticipantRoleCreate(role=role) participant = get_or_create( db_session=db_session, incident_id=incident.id, individual_id=individual.id, service_id=service_id, participant_roles=[participant_role], ) individual.participant.append(participant) incident.participants.append(participant) # we update the commander, reporter, scribe, or liaison foreign key if role == ParticipantRoleType.incident_commander: incident.commander_id = participant.id incident.commanders_location = participant.location elif role == ParticipantRoleType.reporter: incident.reporter_id = participant.id incident.reporters_location = participant.location elif role == ParticipantRoleType.scribe: incident.scribe_id = participant.id elif role == ParticipantRoleType.liaison: incident.liaison_id = participant.id # we add and commit the changes db_session.add(participant) db_session.add(individual) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description= f"{individual.name} added to incident with {participant_role.role} role", incident_id=incident.id, ) return participant
def calculate_incidents_response_cost(db_session: SessionLocal, project: Project): """Calculates and saves the response cost for all incidents.""" response_cost_type = incident_cost_type_service.get_default( db_session=db_session, project_id=project.id) if response_cost_type is None: log.warning( f"A default cost type for response cost does not exist in the {project.name} project. Response costs won't be calculated." ) return # we want to update the response cost of all incidents, all the time incidents = incident_service.get_all(db_session=db_session, project_id=project.id) for incident in incidents: try: # we get the response cost for the given incident incident_response_cost = get_by_incident_id_and_incident_cost_type_id( db_session=db_session, incident_id=incident.id, incident_cost_type_id=response_cost_type.id, ) if incident_response_cost is None: # we create the response cost if it doesn't exist incident_cost_type = IncidentCostTypeRead.from_orm( response_cost_type) incident_cost_in = IncidentCostCreate( incident_cost_type=incident_cost_type, project=project) incident_response_cost = create( db_session=db_session, incident_cost_in=incident_cost_in) # we calculate the response cost amount amount = calculate_incident_response_cost(incident.id, db_session) # we don't need to update the cost amount if it hasn't changed if incident_response_cost.amount == amount: continue # we save the new incident cost amount incident_response_cost.amount = amount incident.incident_costs.append(incident_response_cost) db_session.add(incident) db_session.commit() log.debug( f"Response cost amount for {incident.name} incident has been updated in the database." ) except Exception as e: # we shouldn't fail to update all incidents when one fails log.exception(e)
def create_evergreen_reminder(db_session: SessionLocal, project: Project, owner_email: str, resource_groups: Any): """Contains the logic for evergreen reminders.""" plugin = plugin_service.get_active_instance(db_session=db_session, plugin_type="email", project_id=project.id) if not plugin: log.warning("Evergreen reminder not sent, no email plugin enabled.") return notification_template = EVERGREEN_REMINDER items = [] for resource_type, resources in resource_groups.items(): for resource in resources: weblink = getattr(resource, "weblink", None) if not weblink: weblink = DISPATCH_UI_URL items.append({ "resource_type": resource_type.replace("_", " ").title(), "name": resource.name, "description": getattr(resource, "description", None), "weblink": weblink, }) notification_type = "evergreen-reminder" name = subject = notification_text = "Evergreen Reminder" success = plugin.instance.send( owner_email, notification_text, notification_template, notification_type, name=name, subject=subject, items=items, # plugin expect dicts ) if success: for item in items: item.evergreen_last_reminder_at = datetime.utcnow() db_session.commit() else: log.error(f"Unable to send evergreen message. Email: {owner_email}")
def remove_participant(user_email: str, incident_id: int, db_session: SessionLocal): """Removes a participant.""" # We load the incident incident = incident_service.get(db_session=db_session, incident_id=incident_id) # We get information about the individual contact_plugin = plugin_service.get_active(db_session=db_session, plugin_type="contact") individual_info = contact_plugin.instance.get(user_email) individual_fullname = individual_info["fullname"] log.debug( f"Removing {individual_fullname} from incident {incident.name}...") participant = get_by_incident_id_and_email(db_session=db_session, incident_id=incident_id, email=user_email) if not participant: log.debug( f"Can't remove {individual_fullname}. They're not an active participant of incident {incident.name}." ) return False # We mark the participant as inactive participant.is_active = False # We make the participant renounce to their active roles participant_active_roles = participant_role_service.get_all_active_roles( db_session=db_session, participant_id=participant.id) for participant_active_role in participant_active_roles: participant_role_service.renounce_role( db_session=db_session, participant_role=participant_active_role) # We add and commit the changes db_session.add(participant) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description=f"{participant.individual.name} removed from incident", incident_id=incident_id, ) return True
def add_participant( user_email: str, incident_id: id, db_session: SessionLocal, service: Service = None, role: ParticipantRoleType = None, ): """Adds a participant.""" # We load the incident incident = incident_service.get(db_session=db_session, incident_id=incident_id) # We get or create a new individual individual = individual_service.get_or_create(db_session=db_session, email=user_email) # We create a role for the participant participant_role_in = ParticipantRoleCreate(role=role) participant_role = participant_role_service.create( db_session=db_session, participant_role_in=participant_role_in) # We get or create a new participant participant = get_or_create( db_session=db_session, incident_id=incident.id, individual_id=individual.id, service=service, participant_roles=[participant_role], ) individual.participant.append(participant) incident.participants.append(participant) # We add and commit the changes db_session.add(individual) db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description= f"{individual.name} added to incident with {participant_role.role} role", incident_id=incident_id, ) return participant
def reactivate_participant(user_email: str, incident_id: int, db_session: SessionLocal): """Reactivates a participant.""" # We load the incident incident = incident_service.get(db_session=db_session, incident_id=incident_id) # We get information about the individual contact_plugin = plugin_service.get_active(db_session=db_session, plugin_type="contact") individual_info = contact_plugin.instance.get(user_email) individual_fullname = individual_info["fullname"] log.debug( f"Reactivating {individual_fullname} on incident {incident.name}...") participant = get_by_incident_id_and_email(db_session=db_session, incident_id=incident_id, email=user_email) if not participant: log.debug( f"{individual_fullname} is not an inactive participant of incident {incident.name}." ) return False # We mark the participant as active participant.is_active = True # We create a role for the participant participant_role_in = ParticipantRoleCreate( role=ParticipantRoleType.participant) participant_role = participant_role_service.create( db_session=db_session, participant_role_in=participant_role_in) participant.participant_roles.append(participant_role) # We add and commit the changes db_session.add(participant) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description=f"{individual_fullname} reactivated", incident_id=incident_id, ) return True
def sync_document_terms(db_session: SessionLocal, project: Project): """Performs term extraction from known documents.""" p = plugin_service.get_active_instance( db_session=db_session, plugin_type="storage", project_id=project.id ) if not p: log.debug("Tried to sync document terms but couldn't find any active storage plugins.") return terms = term_service.get_all(db_session=db_session, project_id=project.id).all() log.debug(f"Fetched {len(terms)} terms from database.") term_strings = [t.text.lower() for t in terms if t.discoverable] phrases = build_term_vocab(term_strings) matcher = build_phrase_matcher("dispatch-term", phrases) documents = get_all(db_session=db_session) for doc in documents: log.debug(f"Processing document. Name: {doc.name}") try: if "sheet" in doc.resource_type: mime_type = "text/csv" else: mime_type = "text/plain" doc_text = p.instance.get(doc.resource_id, mime_type) extracted_terms = list(set(extract_terms_from_text(doc_text, matcher))) matched_terms = ( db_session.query(Term) .filter(func.upper(Term.text).in_([func.upper(t) for t in extracted_terms])) .all() ) log.debug(f"Extracted the following terms from {doc.weblink}. Terms: {extracted_terms}") if matched_terms: doc.terms = matched_terms db_session.commit() except Exception as e: # even if one document fails we don't want them to all fail log.exception(e)
def reactivate_participant(user_email: str, incident: Incident, db_session: SessionLocal, service_id: int = None): """Reactivates a participant.""" participant = get_by_incident_id_and_email(db_session=db_session, incident_id=incident.id, email=user_email) if not participant: log.debug( f"{user_email} is not an inactive participant of {incident.name} incident." ) return False log.debug( f"Reactivating {participant.individual.name} on {incident.name} incident..." ) # we get the last active role participant_role = participant_role_service.get_last_active_role( db_session=db_session, participant_id=participant.id) # we create a new role based on the last active role participant_role_in = ParticipantRoleCreate(role=participant_role.role) participant_role = participant_role_service.create( db_session=db_session, participant_role_in=participant_role_in) participant.participant_roles.append(participant_role) if service_id: service = service_service.get(db_session=db_session, service_id=service_id) participant.service = service db_session.add(participant) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description=f"{participant.individual.name} has been reactivated", incident_id=incident.id, ) return True
def create_reminder( db_session: SessionLocal, project: Project, owner_email: str, documents: List[Document] ): """Contains the logic for document evergreen reminders.""" # send email contact_fullname = contact_weblink = DISPATCH_HELP_EMAIL plugin = plugin_service.get_active_instance( db_session=db_session, plugin_type="email", project_id=project.id ) if not plugin: log.warning("Document reminder not sent, no email plugin enabled.") return notification_template = DOCUMENT_EVERGREEN_REMINDER items = [] for doc in documents: items.append( { "name": doc.name, "description": doc.description, "weblink": doc.weblink, } ) notification_type = "document-evergreen-reminder" name = subject = notification_text = "Document Evergreen Reminder" plugin.instance.send( owner_email, notification_text, notification_template, notification_type, name=name, subject=subject, contact_fullname=contact_fullname, contact_weblink=contact_weblink, items=items, # plugin expect dicts ) for doc in documents: doc.evergreen_last_reminder_at = datetime.utcnow() db_session.add(doc) db_session.commit()
def install_plugins(force): """Installs all plugins, or only one.""" from dispatch.database.core import SessionLocal from dispatch.plugin import service as plugin_service from dispatch.plugin.models import Plugin from dispatch.common.utils.cli import install_plugins from dispatch.plugins.base import plugins install_plugins() db_session = SessionLocal() for p in plugins.all(): record = plugin_service.get_by_slug(db_session=db_session, slug=p.slug) if not record: click.secho( f"Installing plugin... Slug: {p.slug} Version: {p.version}", fg="blue") record = Plugin( title=p.title, slug=p.slug, type=p.type, version=p.version, author=p.author, author_url=p.author_url, multiple=p.multiple, description=p.description, ) db_session.add(record) if force: click.secho( f"Updating plugin... Slug: {p.slug} Version: {p.version}", fg="blue") # we only update values that should change record.title = p.title record.version = p.version record.author = p.author record.author_url = p.author_url record.description = p.description record.type = p.type db_session.add(record) db_session.commit()
def auto_tagger(db_session: SessionLocal, project: Project): """Attempts to take existing tags and associate them with incidents.""" tags = tag_service.get_all(db_session=db_session, project_id=project.id).all() log.debug(f"Fetched {len(tags)} tags from database.") tag_strings = [t.name.lower() for t in tags if t.discoverable] phrases = build_term_vocab(tag_strings) matcher = build_phrase_matcher("dispatch-tag", phrases) for incident in get_all(db_session=db_session, project_id=project.id).all(): plugin = plugin_service.get_active_instance( db_session=db_session, project_id=incident.project.id, plugin_type="storage" ) log.debug(f"Processing incident. Name: {incident.name}") doc = incident.incident_document if doc: try: mime_type = "text/plain" text = plugin.instance.get(doc.resource_id, mime_type) except Exception as e: log.debug(f"Failed to get document. Reason: {e}") log.exception(e) continue extracted_tags = list(set(extract_terms_from_text(text, matcher))) matched_tags = ( db_session.query(Tag) .filter(func.upper(Tag.name).in_([func.upper(t) for t in extracted_tags])) .all() ) incident.tags.extend(matched_tags) db_session.commit() log.debug( f"Associating tags with incident. Incident: {incident.name}, Tags: {extracted_tags}" )
def assign_role_flow(incident: "Incident", assignee_email: str, assignee_role: str, db_session: SessionLocal): """Attempts to assign a role to a participant. Returns: str: - "role_assigned", if role assigned. - "role_not_assigned", if not role assigned. - "assignee_has_role", if assignee already has the role. """ # we get the participant that holds the role assigned to the assignee participant_with_assignee_role = participant_service.get_by_incident_id_and_role( db_session=db_session, incident_id=incident.id, role=assignee_role) # we get the participant for the assignee assignee_participant = participant_service.get_by_incident_id_and_email( db_session=db_session, incident_id=incident.id, email=assignee_email) if participant_with_assignee_role is assignee_participant: return "assignee_has_role" if participant_with_assignee_role: # we make the participant renounce to the role that has been given to the assignee participant_active_roles = get_all_active_roles( db_session=db_session, participant_id=participant_with_assignee_role.id) for participant_active_role in participant_active_roles: if participant_active_role.role == assignee_role: renounce_role(db_session=db_session, participant_role=participant_active_role) break # we check if the participant has other active roles participant_active_roles = get_all_active_roles( db_session=db_session, participant_id=participant_with_assignee_role.id) if participant_active_roles.count() == 0: # we give the participant a new participant role add_role( db_session=db_session, participant_id=participant_with_assignee_role.id, participant_role=ParticipantRoleType.participant, ) log.debug( f"We made {participant_with_assignee_role.individual.name} renounce to their {assignee_role} role." ) if assignee_participant: # we make the assignee renounce to the participant role, if they have it participant_active_roles = get_all_active_roles( db_session=db_session, participant_id=assignee_participant.id) for participant_active_role in participant_active_roles: if participant_active_role.role == ParticipantRoleType.participant: renounce_role(db_session=db_session, participant_role=participant_active_role) break # we give the assignee the new role add_role( db_session=db_session, participant_id=assignee_participant.id, participant_role=assignee_role, ) # we update the commander, reporter, scribe, or liaison foreign key if assignee_role == ParticipantRoleType.incident_commander: incident.commander_id = assignee_participant.id elif assignee_role == ParticipantRoleType.reporter: incident.reporter_id = assignee_participant.id elif assignee_role == ParticipantRoleType.scribe: incident.scribe_id = assignee_participant.id elif assignee_role == ParticipantRoleType.liaison: incident.liaison_id = assignee_participant.id # we add and commit the changes db_session.add(incident) db_session.commit() event_service.log( db_session=db_session, source="Dispatch Core App", description= f"{assignee_participant.individual.name} has been assigned the role of {assignee_role}", incident_id=incident.id, ) return "role_assigned" log.debug( f"We were not able to assign the {assignee_role} role to {assignee_email}." ) return "role_not_assigned"