Beispiel #1
0
    def handle_sync_mode(my, share):

        sync_mode = my.get_value("sync_mode")
        share.set_value("sync_mode", sync_mode)

        if sync_mode == 'file':
            ticket = my.get_value("encrypt_key")
            if not ticket:
                ticket = "tactic"

            sync_folder = my.get_value("sync_folder")
            assert (sync_folder)
            share.set_value("base_dir", sync_folder)

            share.set_value("ticket", ticket)
        else:

            host = my.get_value("host")
            if not host:
                raise TacticException("Must defined a host")
            share.set_value("host", host)

            ticket = my.get_value("auth_ticket")
            if not ticket:
                raise TacticException("Must defined a ticket")

            share.set_value("ticket", ticket)
def insert_data_check(server=None, input_data=None):
    department_request_sobject = input_data.get('sobject')

    if not department_request_sobject.get('name'):
        raise TacticException("Name field is required.")

    if not department_request_sobject.get('due_date'):
        raise TacticException("Due Date field is required.")

    if not department_request_sobject.get('description'):
        raise TacticException("Description field is required.")

    # If 'status' is not set, set it to 'in_progress' by default
    if not department_request_sobject.get('status'):
        status = 'in_progress'
    else:
        status = None

    # If 'login' is not in the inserted sobject, insert it using the logged in user's name
    if not department_request_sobject.get('login'):
        login = Environment.get_security().get_login().get_login()
    else:
        login = None

    # If either status or login was set, and update is needed
    if status or login:
        update_dictionary = {}

        if status:
            update_dictionary['status'] = status
        if login:
            update_dictionary['login'] = login

        # Send the update data
        server.update(department_request_sobject.get('__search_key__'), update_dictionary)
Beispiel #3
0
    def check(my):
        project_code = my.kwargs.get('project_code')
        regexs = '^\d|\W'
        m = re.search(r'%s' % regexs, project_code)
        if m:
            if isinstance(project_code, unicode):
                project_code = project_code.encode('utf-8')
            else:
                project_code = unicode(project_code).encode('utf-8')
            raise TacticException(
                '<project_code> [%s] cannot contain special characters or start with a number.'
                % project_code)

        # check to see if this project already exists
        test_project = Project.get_by_code(project_code)
        if test_project:
            if test_project.get_value('s_status') == 'retired':
                raise TacticException(
                    'Project with code [%s] already exists but is retired.' %
                    project_code)
            else:
                raise TacticException(
                    'Project with code [%s] already exists.' % project_code)

        return True
Beispiel #4
0
    def validate(my):

        sandbox_dir_naming = my.get_value('sandbox_dir_naming', no_exception=True)
        dir_naming = my.get_value('dir_naming', no_exception=True)

        if sandbox_dir_naming and sandbox_dir_naming.endswith('/'):
            raise TacticException('sandbox_dir_naming should not end with /')
            
        if dir_naming and dir_naming.endswith('/'):
            raise TacticException('dir_naming should not end with /')
Beispiel #5
0
    def validate(my):
        pat = re.compile('^\d')
        code = my.get_code()
        if not code:
            return
        if pat.search(code):
            raise TacticException('Shot code should not start with a number')

        if my.get_frame_start() > my.get_frame_end():
            raise TacticException('End Frame must be larger than Start Frame')
Beispiel #6
0
    def handle_python(my, node):
        path = my.xml.get_attribute(node, "path")
        if not path:
            raise TacticException("No path found for python in manifest")

        if not path.endswith('.py'):
            raise TacticException("Path should have the .py extension for python in manifest")

        path = "%s/%s" % (my.plugin_dir, path)
        if not os.path.exists(path):
            raise TacticException("Path [%s] does not exist." %path)
Beispiel #7
0
    def check_project(my):

        # check that the project code starts with the prefix
        if not my.project.get_code().startswith("%s_" % my.prefix):
            raise TacticException(
                "Project code [%s] does not start with prefix [%s]" %
                (my.project_code, my.prefix))

        # check that the project type is the same as the project code
        if not my.project_code != my.project_type:
            raise TacticException(
                "Project code [%s] does not match the project_type [%s]" %
                (my.project_code, my.project_type))
Beispiel #8
0
    def execute(my):

        my.base_dir = my.kwargs.get("base_dir")
        if not my.base_dir:
            my.base_dir = Environment.get_template_dir()

        my.project_code = my.kwargs.get("project_code")
        if not my.project_code:
            my.project_code = Project.get_project_code()

        assert my.project_code

        # project code can be called anything, and we want to have a _template suffix for the template code
        #my.plugin_code = "%s_template" % my.project_code

        #my.template_project_code = re.sub( '_template$', '', my.plugin_code)
        my.template_project_code = my.project_code
        my.project = Project.get_by_code(my.project_code)
        if not my.project:
            raise TacticException('This project [%s] does not exist' %
                                  my.project_code)

        my.project_type = my.project.get_value("type")
        if not my.project_type:
            my.project_type = my.project_code
        Project.set_project(my.project_code)

        my.export_template()
Beispiel #9
0
    def execute(self):

        self.base_dir = self.kwargs.get("base_dir")
        if not self.base_dir:
            self.base_dir = Environment.get_template_dir()

        self.project_code = self.kwargs.get("project_code")
        if not self.project_code:
            self.project_code = Project.get_project_code()

        assert self.project_code

        # project code can be called anything, and we want to have a _template suffix for the template code
        #self.plugin_code = "%s_template" % self.project_code

        #self.template_project_code = re.sub( '_template$', '', self.plugin_code)
        self.template_project_code = self.project_code
        self.project = Project.get_by_code(self.project_code)
        if not self.project:
            raise TacticException('This project [%s] does not exist' %
                                  self.project_code)

        self.project_type = self.project.get_value("type")
        if not self.project_type:
            self.project_type = self.project_code
        Project.set_project(self.project_code)

        self.export_template()
Beispiel #10
0
    def execute(my):
        my.search_key_list = my.kwargs.get('search_key_list')
        web = WebContainer.get_web()
        skip_duplicated = web.get_form_value('skip_duplicated') == 'on'
        pipeline_mode = web.get_form_value('pipeline_mode')

        sobjects = SearchKey.get_by_search_keys(my.search_key_list)
        count = 0
        offset = 0
        for sobject in sobjects:
            if isinstance(sobject, Task):
                raise TacticException('Creation of task for [Task] is not allowed')
            sk = SearchKey.get_by_sobject(sobject)
            if not sobject.has_value('pipeline_code'):
                #raise TacticException('Creation of task is not allowed for item with no pipeline_code attribute.')
                pipeline_code = '__default__'
                sobject.set_value("pipeline_code", pipeline_code)
            else:
                pipeline_code = sobject.get_value('pipeline_code')
            input_name = '%s|task_process'% pipeline_code
            
            contexts = []
            process_names = web.get_form_values(input_name)
            process_names = [name for name in process_names if name]
            if pipeline_mode == 'context':
                # when pipeline_mode is context, we only specify contexts
                # in add_initial_tasks
                contexts = process_names[:]
                process_names = []
            tasks = Task.add_initial_tasks(sobject, sobject.get_value('pipeline_code'),
                    processes=process_names, contexts=contexts, skip_duplicate=skip_duplicated, mode=pipeline_mode, start_offset=offset)

            count += len(tasks)
            offset += 5
        my.add_description("%s Tasks added in total." % count)
Beispiel #11
0
    def execute(my):
        web = my.get_web()
        keys = web.get_form_keys()
        file_name = my.kwargs.get("file_name")

        # process and get the uploaded files
        dir = Environment.get_upload_dir()
        license_file = "%s/%s" % (dir, file_name)
        if not os.path.exists(license_file):
            raise TacticException("Error retrieving the license file in [%s]" %
                                  license_file)

        std_name = 'tactic-license.xml'

        head, file_name = os.path.split(license_file)
        # no restrictions for license file
        #if file_name != std_name:
        #    raise TacticException("License file name should be named tactic-license.xml. The file given is [%s]" %file_name)

        license_dir = Environment.get_license_dir()
        current_license = "%s/%s" % (license_dir, std_name)
        if os.path.exists(current_license):
            FileUndo.remove(current_license)
        FileUndo.move(license_file, current_license)

        my.add_description('Renewed license file')
        security = Environment.get_security()
        security.reread_license()
Beispiel #12
0
    def prod_texture(my):

        parts = []

        code = my.sobject.get_code()
        parts.append(code)

        # handle file_type for icons
        file_type = my.get_file_type()
        if file_type == 'web' or file_type == 'icon':
            parts.append(file_type)

        file_name = my.add_ending(parts, auto_version=True)

        if my.file_object.get_file_range():
            orig_file_name = my.file_object.get_file_name()
            pat = re.compile('\.(#+)\.')
            m = pat.search(orig_file_name)
            if m:
                padding = len(m.groups()[0])
                padding_str = '#' * padding
            else:
                raise TacticException('no # found in the file name')
            base, ext = os.path.splitext(file_name)
            file_name = "%s.%s%s" % (base, padding_str, ext)

        return file_name
Beispiel #13
0
 def check(my):
     #search_type_obj = SearchType.get(my.search_type)
     columns = SearchType.get_columns(my.search_type)
     if my.attr_name not in columns:
         raise TacticException('[%s] does not exist in this table [%s]' %
                               (my.attr_name, my.search_type))
     return True
Beispiel #14
0
    def get_results(self):
        code = self.kwargs.get("code")
        script_path = self.kwargs.get("script_path")
        file_path = self.kwargs.get("file_path")

        # if a script path is specified, then get it from the custom_script
        # table
        if script_path:

            folder = os.path.dirname(script_path)
            title = os.path.basename(script_path)

            search = Search("config/custom_script")
            search.add_filter("folder", folder)
            search.add_filter("title", title)
            custom_script = search.get_sobject()
            if not custom_script:
                raise TacticException(
                    "Custom script with path [%s/%s] does not exist" %
                    (folder, title))
            code = custom_script.get_value("script")

        elif file_path:
            f = open(file_path)
            code = f.read()
            f.close()

        wrapper = JsWrapper.get()
        results = wrapper.execute_func(code, self.kwargs)

        return results
Beispiel #15
0
    def execute(my):
        server = TacticServerStub.get()
        server.start("Starting city insert trigger",
                     "On inserting seoul, insert incheon")

        try:

            city_key = my.get_input_value("search_key")
            mode = my.get_input_value("mode")
            if mode == 'delete':
                return
            city = server.get_by_search_key(city_key)
            if city.get('code') == 'seoul':
                # make sure triggers=False to avoid infinite loop
                server.insert('unittest/city', {
                    'code': 'incheon',
                    'country_code': 'korea'
                },
                              triggers=False)

            if city.get('code') == 'incheon':
                raise TacticException(
                    'Should not have reached this point and fired this trigger.'
                )

        except:
            server.abort()
            raise
        else:
            server.finish()
Beispiel #16
0
    def get_match(my, name):
        match = my.matches.get(name)
        if not match:
            raise TacticException(
                'Failed to process this portion [%s] of the code name ' % name)

        return match
Beispiel #17
0
    def handle_include(my, node):
        path = my.xml.get_attribute(node, "path")
        if not path:
            raise TacticException("No path found for include in manifest")

        path = "%s/%s" % (my.plugin_dir, path)

        if path.endswith(".py"):
            from tactic.command import PythonCmd
            cmd = PythonCmd(file_path=path)
            manifest = cmd.execute()

        if not manifest:
            print "No manifest discovered in [%s]" %path
            return

        xml = Xml()
        xml.read_string(manifest)
        nodes = xml.get_nodes("manifest/*")

        sobjects = []
        for i, node in enumerate(nodes):
            name = my.xml.get_node_name(node)
            if name == 'sobject':
                dumped_sobjects = my.handle_sobject(node)
                if not dumped_sobjects:
                    dumped_sobjects = []
                sobjects.extend(dumped_sobjects)
            elif name == 'search_type':
                my.handle_search_type(node)
            elif name == 'include':
                my.handle_include(node)
Beispiel #18
0
    def add_smart_menu_set(html_wdg, menus_in):
        from menu_wdg import Menu
        if type(menus_in) == list:
            # default_menu_specs
            smenu_set = SmartMenuSetWdg(default_menu_specs=menus_in)

            #smenu_set = SmartMenuSetWdg()
            #for menus_list in menus_in:
            #    if isinstance(menus_list, Menu):
            #        menus_list = [menus_list.get_data()]
            #    smenu_set.add_subset_menu_specs( ss_tag_suffix, menus_list )

        elif type(menus_in) == dict:
            smenu_set = SmartMenuSetWdg()
            for ss_tag_suffix, menus_list in menus_in.iteritems():
                if isinstance(menus_list, Menu):
                    menus_list = [menus_list.get_data()]
                    smenu_set.add_subset_menu_specs(ss_tag_suffix, menus_list)
                elif type(menus_list) == list:
                    new_menus_list = []
                    for x in menus_list:
                        if isinstance(x, Menu):
                            new_menus_list.append(x.get_data())
                        else:
                            new_menus_list.append(x)
                    smenu_set.add_subset_menu_specs(ss_tag_suffix,
                                                    new_menus_list)
                else:
                    smenu_set.add_subset_menu_specs(ss_tag_suffix, menus_list)
        else:
            raise TacticException("SmartMenu.add_smart_menu_set() accepts only a list or dictionary as the " \
                                  "second parameter ('menus_in')")
        if html_wdg:
            html_wdg.add(smenu_set)
        return smenu_set
Beispiel #19
0
    def handle_info(my, share):

        code = my.get_value("code")
        if not code:
            raise TacticException("Cannot have empty code")
        share.set_value("code", code)

        description = my.get_value("description")
        if description:
            share.set_value("description", description)

        share.set_value("state", "online")

        project_code = my.get_value("project")
        if not project_code or project_code == "__all__":
            project_code = "*"

        # default access rules are pretty open
        access_rules = """
<rules>
<rule group="project" code="%s" access="allow"/>
<rule group="search_type" code="*" access="allow"/>
</rules>
        """ % my.project_code
        share.set_value("access_rules", access_rules)
Beispiel #20
0
    def get_parent_dir(self, search_type=None, context=None, sobject=None):
        from project import Project

        if not sobject:
            sobject = self.sobject
        if search_type:
            parent = sobject.get_parent(search_type)
        else:
            search_type = sobject.get_value("search_type")
            search_id = sobject.get_value("search_id")
            parent = Search.get_by_id(search_type, search_id)

        if not parent:
            raise TacticException("No parent exists for '%s', possibly a result of Shot rename or removal." % sobject.get_code())

        # just use the latest of the context desired
        if context:
            search_id = parent.get_id()
            search_type = parent.get_search_type()
            snapshot = Snapshot.get_latest(search_type, search_id, context)
        else:
            # basically this means that without a parent context, there is
            # no way to know the directory this is in.
            snapshot = None
        dirs = Project._get_dir( self.protocol,parent,snapshot,None )
        dirs = dirs.split("/")
        return dirs
Beispiel #21
0
    def get_def_config(my, def_xml=None):
        def_confg = None

        my.def_view = my.kwargs.get('definition')
        if my.def_view:
            #raise TacticException("No definition view defined in custom layout with view [%s]" % my.view)

            my.search_type = "CustomLayoutWdg"
            search = Search("config/widget_config")
            search.add_filter("search_type", my.search_type)
            search.add_filter("view", my.def_view)
            def_db_config = search.get_sobject()
            if not def_db_config:
                raise TacticException("Definition config [%s] not defined" % my.def_view)
            def_xml = def_db_config.get_xml()
            def_config = WidgetConfig.get("definition", xml=def_xml)


        # also look inline to see if there are any definitions        
        if def_xml:
            # just use the passed in xml for a definition
            def_config = WidgetConfig.get(my.view, xml=def_xml)


        return def_config
    def execute(self):
        # Get the task sobject (in this case, should be attached to twog/department_request)
        task_sobject = self.input.get('sobject')

        # Do a sanity check on the search_type to make sure we're working with a twog/department_request
        search_type = task_sobject.get('search_type')

        if search_type != u'twog/department_request?project=twog':
            raise TacticException(
                "Something went wrong. A trigger was called on a task that it should not have been."
                "Trigger Name: {0}".format(self.__class__.__name__))

        # Get the twog/department_request sobject
        search_code = task_sobject.get('search_code')
        department_request_search = Search('twog/department_request')
        department_request_search.add_code_filter(search_code)
        department_request_sobject = department_request_search.get_sobject()

        # Task process should be either 'Request' or 'Approval'
        task_process = task_sobject.get('process').lower()
        # For this trigger, only 'Complete', 'Rejected', and 'Approved' apply
        task_status = task_sobject.get('status').lower()
        # Get a server instance to send the data
        server = TacticServerStub.get()

        if task_process == 'request' and task_status == 'complete':
            # Check if the department request has its 'Response' column filled out. If not, raise an error
            if not department_request_sobject.get('response'):
                raise TacticException(
                    "Before marking the request as 'Complete', you must fill out the 'Response' "
                    "column.")

            # Mark the twog/department_request status as needing approval
            server.update(department_request_sobject.get_search_key(),
                          data={'status': 'approval'})
        elif task_process == 'approval':
            if task_status == 'rejected':
                # Need to set the twog/department_request status back to 'in_progress' so that it appears on the
                # department's list again. Setting the task status to 'Revise' is not handled here (the pipeline
                # takes care of that).
                server.update(department_request_sobject.get_search_key(),
                              data={'status': 'in_progress'})
            elif task_status == 'approved':
                # If the Approval task is marked as 'approved', then the request is finished. Mark the
                # twog/department_request status as 'complete' so it disappears from all views.
                server.update(department_request_sobject.get_search_key(),
                              data={'status': 'complete'})
Beispiel #23
0
 def validate(my):
     '''The project code cannot just be an integer'''
     try:
         int(my.get_value("code"))
     except ValueError:
         pass
     else:
         raise TacticException('Project code cannot be a number')
Beispiel #24
0
 def check(my):
     web = WebContainer.get_web()
     my.login = web.get_form_value("login")
     if my.login == 'admin':
         error_msg = "You are not allowed to reset admin password."
         web.set_form_value(ResetPasswordWdg.MSG, error_msg)
         raise TacticException(error_msg)
         return False
     return True
Beispiel #25
0
    def execute(self):
        self.section = None

        # make sure tmp config is unset.
        Config.unset_tmp_config()
        Config.reload_config()

        web = WebContainer.get_web()

        # read the current config file.

        # copy config to the path
        config_path = Config.get_config_path()
        if not os.path.exists(config_path):
            print "Installing default config file"

            dirname = os.path.dirname(config_path)
            if not os.path.exists(dirname):
                os.makedirs(dirname)

            if os.name == 'nt':
                osname = 'win32'
            else:
                osname = 'linux'

            install_dir = Environment.get_install_dir()
            install_config_path = "%s/src/install/start/config/tactic_%s-conf.xml" % (
                install_dir, osname)

            shutil.copy(install_config_path, dirname)

        try:
            self.configure_db()
            self.configure_install()
            self.configure_mail_services()
            self.configure_gen_services()
            self.configure_asset_dir()
            self.configure_palette()
            self.configure_security()
        except Exception as e:
            raise TacticException('Error in [%s]: %s' %
                                  (self.section, e.__str__()))
        # FIXME: if this all fails, then revert back

        self.save_config()

        # after saving the config, test the database
        self.load_bootstrap()

        # remove the first run file
        data_dir = Environment.get_data_dir()
        path = "%s/first_run" % data_dir
        if os.path.exists(path):
            os.unlink(path)

        self.restart_program()
Beispiel #26
0
    def handle_search_type(my, node):

        search_type = my.xml.get_attribute(node, "code")
        if not search_type:
            raise TacticException("No code found for search type in manifest")

        path = my.xml.get_attribute(node, "path")

        if not path:
            path = "%s.spt" % search_type.replace("/", "_")

        path = "%s/%s" % (my.plugin_dir, path)
        
        if os.path.exists(path):
            os.unlink(path)

        # dump out search type registration
        search = Search("sthpw/search_object")
        search.add_filter("search_type", search_type)
        sobject = search.get_sobject()
        if not sobject:
            raise TacticException("Search type [%s] does not exist" % search_type)

        dumper = TableDataDumper()
        dumper.set_delimiter("#-- Start Entry --#", "#-- End Entry --#")
        dumper.set_include_id(False)
        dumper.set_sobjects([sobject])
        dumper.dump_tactic_inserts(path, mode='sobject')


        ignore_columns = Xml.get_attribute(node, "ignore_columns")
        if ignore_columns:
            ignore_columns = ignore_columns.split(",")
            ignore_columns = [x.strip() for x in ignore_columns]
        else:
            ignore_columns = []


        # dump out the table definition
        dumper = TableSchemaDumper(search_type)
        dumper.set_delimiter("#-- Start Entry --#", "#-- End Entry --#")
        dumper.set_ignore_columns(ignore_columns)
        dumper.dump_to_tactic(path, mode='sobject')
Beispiel #27
0
    def handle_python(my, node):
        '''during uninstall, handle the python undo_path'''
        path = my.xml.get_attribute(node, "undo_path")
        
        # if no path, then nothing to undo
        if not path:
            print "No undo_path defined for this python node"
            return

        if not path.endswith('.py'):
            raise TacticException("Path should have the .py extension for python in manifest")

        path = "%s/%s" % (my.plugin_dir, path)
        if not os.path.exists(path):
            raise TacticException("Undo Path [%s] does not exist python in manifest" %path)
        if path.endswith(".py"):
            from tactic.command import PythonCmd
            cmd = PythonCmd(file_path=path)
            cmd.execute()
    def execute(self):
        input = self.get_input()

        search_key = input.get("search_key")
        task = Search.get_by_search_key(search_key)
        parent = task.get_parent()
        if not parent:
            raise TacticException("Task parent not found.")

        # get the definition of the trigger
        trigger_sobj = self.get_trigger_sobj()
        data = trigger_sobj.get_value("data")
        try:
            data = jsonloads(data)
        except:
            raise TacticException("Incorrect formatting of trigger [%s]." %
                                  trigger_sobj.get_value("code"))

        # check against source status if present
        src_status = data.get("src_status")
        if src_status:
            task_status = task.get_value("status")
            if task_status != src_status:
                return

        process_names = data.get("output")
        if not process_names:
            return

        # only create new task if another of the same
        # process does not already exist
        search = Search("sthpw/task")
        search.add_filters("process", process_names)
        search.add_parent_filter(parent)
        search.add_project_filter()
        tasks = search.get_sobjects()
        existing_processes = [x.get_value("process") for x in tasks]

        for process in process_names:
            if process in existing_processes:
                continue
            else:
                Task.create(parent, process, start_date=None, end_date=None)
Beispiel #29
0
    def get_project_db_resource(my):
        # get the db resource for attached to this particular project.
        # Not the db_resource for "sthpw/project" for which
        # project.get_db_resource() does
        key = "Project:db_resource:%s" % my.get_code()
        resource = Container.get(key)
        if resource != None:
            return resource

        # the project defines the resource
        database = my.get_database_name()
        assert database

        if database == 'sthpw':
            # get if from the config
            db_resource_code = None
        else:
            db_resource_code = my.get_value("db_resource", no_exception=True)

        if not db_resource_code:
            # this could be any project, not just sthpw
            # already looked at cache, so set use_cache to False
            db_resource = DbResource.get_default(database, use_cache=False)
            Container.put(key, db_resource)
            return db_resource
        #elif isinstance(db_resource_code, DbResource):
        elif DbResource.is_instance(db_resource_code):
            db_resource = db_resource_code
            Container.put(key, db_resource)
            return db_resource

        db_resource_code = db_resource_code.strip()
        search = Search("sthpw/db_resource")
        search.add_filter("code", db_resource_code)
        db_resource_sobj = search.get_sobject()
        if not db_resource_sobj:
            raise TacticException("Database Resource [%s] does not exist" %
                                  db_resource_code)

        host = db_resource_sobj.get_value("host")
        vendor = db_resource_sobj.get_value("vendor")
        host = db_resource_sobj.get_value("host")
        port = db_resource_sobj.get_value("port")
        user = db_resource_sobj.get_value("login")
        password = db_resource_sobj.get_value("password")

        db_resource = DbResource(database=database,
                                 host=host,
                                 port=port,
                                 vendor=vendor,
                                 password=password)
        Container.put(key, db_resource)

        return db_resource
Beispiel #30
0
    def delete(my, log=True):
        '''This is for undo'''
        # TODO: the should probably be clearer!!!!
        if log == False:
            super(Shot, my).delete(log)
            return

        # An asset can only be deleted if only icon snapshots exist
        snapshots = Snapshot.get_by_sobject(my)

        only_icons = True
        for snapshot in snapshots:
            context = snapshot.get_value("context")
            if context != my.get_icon_context():
                only_icons = False

        if not only_icons:
            raise TacticException("Cannot delete because snapshots exist")

        # only delete if not tasks have been assigned
        tasks = Task.get_by_sobject(my)
        has_assigned = False
        for task in tasks:
            assigned = task.get_value("assigned")
            if assigned != "" and assigned != "None":
                has_assigned = True

        if has_assigned:
            raise TacticException(
                "Cannot delete because tasks have been assigned")

        # delete tasks and icons
        for snapshot in snapshots:
            snapshot.delete()
        for task in tasks:
            task.delete()

        my.description = "Deleted '%s', search_type '%s'" % (
            my.get_code(), my.get_search_type)

        super(Shot, my).delete(log)