Exemple #1
0
class SourceFile(TextFile):
    project = models.ForeignKey('Project', related_name='source_files')
    file_name = models.CharField(max_length=100,
                                 validators=regexes.validator(
                                     'source_file_name',
                                     _('Invalid file name.')))
    folder = 'sources'

    TARGETS = (
        ('app', _('App')),
        ('worker', _('Worker')),
    )
    target = models.CharField(max_length=10, choices=TARGETS, default='app')

    @property
    def project_path(self):
        if self.target == 'app':
            return 'src/%s' % self.file_name
        else:
            return 'worker_src/%s' % self.file_name

    class Meta(IdeModel.Meta):
        unique_together = (('project', 'file_name'))
Exemple #2
0
class Project(IdeModel):
    owner = models.ForeignKey(User)
    name = models.CharField(max_length=50)
    last_modified = models.DateTimeField(auto_now_add=True)

    PROJECT_TYPES = (
        ('native', _('Pebble C SDK')),
        ('simplyjs', _('Simply.js')),
        ('pebblejs', _('Pebble.js (beta)')),
        ('package', _('Pebble Package')),
        ('rocky', _('Rocky.js')),
    )
    project_type = models.CharField(max_length=10,
                                    choices=PROJECT_TYPES,
                                    default='native')

    SDK_VERSIONS = (
        ('2', _('SDK 2 (obsolete)')),
        ('3', _('SDK 4 beta')),
    )
    sdk_version = models.CharField(max_length=6,
                                   choices=SDK_VERSIONS,
                                   default='2')

    # New settings for 2.0
    app_uuid = models.CharField(max_length=36,
                                blank=True,
                                null=True,
                                default=generate_half_uuid,
                                validators=regexes.validator(
                                    'uuid', _('Invalid UUID.')))
    app_company_name = models.CharField(max_length=100, blank=True, null=True)
    app_short_name = models.CharField(max_length=100, blank=True, null=True)
    app_long_name = models.CharField(max_length=100, blank=True, null=True)
    app_version_label = models.CharField(max_length=40,
                                         blank=True,
                                         null=True,
                                         default='1.0')
    app_is_watchface = models.BooleanField(default=False)
    app_is_hidden = models.BooleanField(default=False)
    app_is_shown_on_communication = models.BooleanField(default=False)
    app_capabilities = models.CharField(max_length=255, blank=True, null=True)
    app_keys = models.TextField(default="{}")
    app_jshint = models.BooleanField(default=False)
    app_platforms = models.TextField(max_length=255, blank=True, null=True)
    app_modern_multi_js = models.BooleanField(default=True)
    app_keywords = models.TextField(default='[]')

    app_capability_list = property(
        lambda self: self.app_capabilities.split(','))
    app_platform_list = property(lambda self: self.app_platforms.split(',')
                                 if self.app_platforms else [])

    OPTIMISATION_CHOICES = (
        ('0', 'None'),
        ('1', 'Limited'),
        ('s', 'Prefer smaller'),
        ('2', 'Prefer faster'),
        ('3', 'Aggressive (faster, bigger)'),
    )

    optimisation = models.CharField(max_length=1,
                                    choices=OPTIMISATION_CHOICES,
                                    default='s')

    github_repo = models.CharField(max_length=100, blank=True, null=True)
    github_branch = models.CharField(max_length=100, blank=True, null=True)
    github_last_sync = models.DateTimeField(blank=True, null=True)
    github_last_commit = models.CharField(max_length=40, blank=True, null=True)
    github_hook_uuid = models.CharField(max_length=36, blank=True, null=True)
    github_hook_build = models.BooleanField(default=False)

    project_dependencies = models.ManyToManyField("Project")

    def __init__(self, *args, **kwargs):
        super(IdeModel, self).__init__(*args, **kwargs)
        # For SDK3+, default to array-based message keys.
        if self.sdk_version != '2' and self.app_keys == '{}':
            self.app_keys = '[]'
        if self.sdk_version == '2':
            self.app_modern_multi_js = False
        if self.project_type == 'package' and self.app_version_label == '1.0':
            self.app_version_label = '1.0.0'

    def set_dependencies(self, dependencies):
        """ Set the project's dependencies from a dictionary.
        :param dependencies: A dictionary of dependency->version
        """
        with transaction.atomic():
            Dependency.objects.filter(project=self).delete()
            for name, version in dependencies.iteritems():
                dep = Dependency.objects.create(project=self,
                                                name=name,
                                                version=version)
                dep.save()

    def set_interdependencies(self, interdependences):
        with transaction.atomic():
            self.project_dependencies.clear()
            for project_id in interdependences:
                project = Project.objects.get(pk=project_id,
                                              owner=self.owner,
                                              project_type='package')
                self.project_dependencies.add(project)

    @property
    def npm_name(self):
        """ Get the project's app_short_name as a valid NPM package name. """
        name = self.app_short_name.lower()
        # Remove any invalid characters from the end
        name = re.sub(r'[^a-z0-9._]+$', '', name)
        # Any strings of invalid characters in the middle are converted to dashes
        name = re.sub(r'[^a-z0-9._]+', '-', name)
        # The name cannot start with [ ._] or end with spaces.
        name = name.lstrip(' ._').rstrip()
        return name

    @property
    def keywords(self):
        """ Get the project's keywords as a list of strings """
        return json.loads(self.app_keywords)

    @keywords.setter
    def keywords(self, value):
        """ Set the project's keywords from a list of strings """
        self.app_keywords = json.dumps(value)

    def get_dependencies(self, include_interdependencies=True):
        """ Get the project's dependencies as a dictionary
        :return: A dictionary of dependency->version
        """
        dependencies = {d.name: d.version for d in self.dependencies.all()}
        if include_interdependencies:
            for project in self.project_dependencies.all():
                dependencies[project.npm_name] = project.last_build.package_url
        return dependencies

    @property
    def uses_array_message_keys(self):
        return isinstance(json.loads(self.app_keys), list)

    def get_parsed_appkeys(self):
        """ Get the project's app keys, or raise an error of any are invalid.
        :return: A list of (appkey, value) tuples, where value is either a length or a size, depending on the kind of appkey.
        """
        app_keys = json.loads(self.app_keys)
        if isinstance(app_keys, dict):
            return sorted(app_keys.iteritems(), key=lambda x: x[1])
        else:
            parsed_keys = []
            for appkey in app_keys:
                parsed = re.match(regexes.C_IDENTIFIER_WITH_INDEX, appkey)
                if not parsed:
                    raise ValueError("Bad Appkey %s" % appkey)
                parsed_keys.append((parsed.group(1), parsed.group(2) or 1))
            return parsed_keys

    def get_last_build(self):
        try:
            return self.builds.order_by('-id')[0]
        except IndexError:
            return None

    def get_menu_icon(self):
        try:
            return self.resources.filter(is_menu_icon=True)[0]
        except IndexError:
            return None

    def has_platform(self, platform):
        return self.app_platforms is None or platform in self.app_platform_list

    @property
    def semver(self):
        """ Get the app's version label formatted as a semver. """
        if self.project_type == 'package':
            try:
                # Packages should have semver app_versions_labels...
                parse_semver(self.app_version_label)
                return self.app_version_label
            except ValueError as e:
                # but if they don't, we try to convert it from an app-style version label.
                try:
                    version_to_semver(self.app_version_label)
                except:
                    raise e
        return version_to_semver(self.app_version_label)

    @semver.setter
    def semver(self, value):
        """ Set the app's version label from a semver string. """
        if self.project_type == 'package':
            # This throws an error if the semver is invalid.
            parse_semver(value)
            self.app_version_label = value
        else:
            self.app_version_label = semver_to_version(value)

    @property
    def supported_platforms(self):
        supported_platforms = ["aplite"]
        if self.sdk_version != '2':
            supported_platforms.extend(["basalt", "chalk"])
            if self.project_type != 'pebblejs':
                supported_platforms.extend(["diorite", "emery"])
        return supported_platforms

    @property
    def resources_path(self):
        return 'src/resources' if self.project_type == 'package' else 'resources'

    @property
    def is_standard_project_type(self):
        return self.project_type in {'native', 'package', 'rocky'}

    @property
    def pkjs_entry_point(self):
        if self.project_type in {'package', 'rocky'}:
            return 'index.js'
        elif self.project_type == 'native' and self.app_modern_multi_js:
            if self.source_files.filter(target='pkjs',
                                        file_name='index.js').exists():
                return 'index.js'
            elif self.source_files.filter(target='pkjs',
                                          file_name='app.js').exists():
                return 'app.js'
            else:
                return 'index.js'
        else:
            return None

    def clean(self):
        is_sdk_2 = self.sdk_version == "2"
        if is_sdk_2 and self.uses_array_message_keys:
            raise ValidationError(
                _("SDK2 appKeys must be an object, not a list."))
        if self.project_type != 'package':
            try:
                parse_sdk_version(self.app_version_label)
            except ValueError:
                raise ValidationError(
                    _("Invalid version string. Versions should be major[.minor]."
                      ))
        if self.project_type == 'package':
            try:
                parse_semver(self.app_version_label)
            except ValueError:
                raise ValidationError(
                    _("Invalid version string. Versions should be major.minor.patch"
                      ))
            if is_sdk_2:
                raise ValidationError(
                    _("Packages are not available for SDK 2"))
            if not self.app_modern_multi_js:
                raise ValidationError(
                    _("Packages must use CommonJS-style JS Handling."))
        elif self.project_type == 'rocky':
            if is_sdk_2:
                raise ValidationError(_("RockyJS is not available for SDK 2"))
            if not self.uses_array_message_keys:
                raise ValidationError(
                    _("RockyJS projects must use array based appmessage keys"))
            if not self.app_modern_multi_js:
                raise ValidationError(
                    _("RockyJS projects must use CommonJS-style JS Handling."))

    last_build = property(get_last_build)
    menu_icon = property(get_menu_icon)

    def __unicode__(self):
        return u"%s" % self.name
Exemple #3
0
class BuildResult(IdeModel):

    STATE_WAITING = 1
    STATE_FAILED = 2
    STATE_SUCCEEDED = 3
    STATE_CHOICES = ((STATE_WAITING, _('Pending')),
                     (STATE_FAILED, _('Failed')), (STATE_SUCCEEDED,
                                                   _('Succeeded')))

    DEBUG_INFO_MAP = {
        'aplite': ('debug_info.json', 'worker_debug_info.json'),
        'basalt': ('basalt_debug_info.json', 'basalt_worker_debug_info.json'),
        'chalk': ('chalk_debug_info.json', 'chalk_worker_debug_info.json'),
    }
    DEBUG_APP = 0
    DEBUG_WORKER = 1

    project = models.ForeignKey(Project, related_name='builds')
    uuid = models.CharField(max_length=36,
                            default=lambda: str(uuid.uuid4()),
                            validators=regexes.validator(
                                'uuid', _('Invalid UUID.')))
    state = models.IntegerField(choices=STATE_CHOICES, default=STATE_WAITING)
    started = models.DateTimeField(auto_now_add=True, db_index=True)
    finished = models.DateTimeField(blank=True, null=True)

    def _get_dir(self):
        if settings.AWS_ENABLED:
            return '%s/' % self.uuid
        else:
            path = '%s%s/%s/%s/' % (settings.MEDIA_ROOT, self.uuid[0],
                                    self.uuid[1], self.uuid)
            if not os.path.exists(path):
                os.makedirs(path)
            return path

    def get_url(self):
        if settings.AWS_ENABLED:
            return "%s%s/" % (settings.MEDIA_URL, self.uuid)
        else:
            return '%s%s/%s/%s/' % (settings.MEDIA_URL, self.uuid[0],
                                    self.uuid[1], self.uuid)

    def get_pbw_filename(self):
        return '%swatchface.pbw' % self._get_dir()

    def get_build_log(self):
        return '%sbuild_log.txt' % self._get_dir()

    def get_pbw_url(self):
        return '%swatchface.pbw' % self.get_url()

    def get_build_log_url(self):
        return '%sbuild_log.txt' % self.get_url()

    def get_debug_info_filename(self, platform, kind):
        return self._get_dir() + self.DEBUG_INFO_MAP[platform][kind]

    def get_debug_info_url(self, platform, kind):
        return self.get_url() + self.DEBUG_INFO_MAP[platform][kind]

    def get_simplyjs(self):
        return '%ssimply.js' % self._get_dir()

    def get_simplyjs_url(self):
        return '%ssimply.js' % self.get_url()

    def save_build_log(self, text):
        if not settings.AWS_ENABLED:
            with open(self.build_log, 'w') as f:
                f.write(text)
        else:
            s3.save_file('builds',
                         self.build_log,
                         text,
                         public=True,
                         content_type='text/plain')

    def read_build_log(self):
        if not settings.AWS_ENABLED:
            with open(self.build_log, 'r') as f:
                return f.read()
        else:
            return s3.read_file('builds', self.build_log)

    def save_debug_info(self, json_info, platform, kind):
        text = json.dumps(json_info)
        if not settings.AWS_ENABLED:
            with open(self.get_debug_info_filename(platform, kind), 'w') as f:
                f.write(text)
        else:
            s3.save_file('builds',
                         self.get_debug_info_filename(platform, kind),
                         text,
                         public=True,
                         content_type='application/json')

    def save_pbw(self, pbw_path):
        if not settings.AWS_ENABLED:
            shutil.move(pbw_path, self.pbw)
        else:
            s3.upload_file('builds',
                           self.pbw,
                           pbw_path,
                           public=True,
                           download_filename='%s.pbw' %
                           self.project.app_short_name.replace('/', '-'))

    def save_simplyjs(self, javascript):
        if not settings.AWS_ENABLED:
            with open(self.simplyjs, 'w') as f:
                f.write(javascript)
        else:
            s3.save_file('builds',
                         self.simplyjs,
                         javascript,
                         public=True,
                         content_type='text/javascript')

    pbw = property(get_pbw_filename)
    build_log = property(get_build_log)

    pbw_url = property(get_pbw_url)
    build_log_url = property(get_build_log_url)

    simplyjs = property(get_simplyjs)
    simplyjs_url = property(get_simplyjs_url)

    def get_sizes(self):
        sizes = {}
        for size in self.sizes.all():
            sizes[size.platform] = {
                'total': size.total_size,
                'app': size.binary_size,
                'resources': size.resource_size,
                'worker': size.worker_size,
            }
        return sizes
Exemple #4
0
class Project(IdeModel):
    owner = models.ForeignKey(User)
    name = models.CharField(max_length=50)
    last_modified = models.DateTimeField(auto_now_add=True)

    PROJECT_TYPES = (
        ('native', _('Pebble C SDK')),
        ('simplyjs', _('Simply.js')),
        ('pebblejs', _('Pebble.js (beta)')),
    )
    project_type = models.CharField(max_length=10,
                                    choices=PROJECT_TYPES,
                                    default='native')

    SDK_VERSIONS = (
        ('2', _('SDK 2 (Pebble, Pebble Steel)')),
        ('3', _('SDK 3 beta (Pebble Time)')),
    )
    sdk_version = models.CharField(max_length=6,
                                   choices=SDK_VERSIONS,
                                   default='2')

    # New settings for 2.0
    app_uuid = models.CharField(max_length=36,
                                blank=True,
                                null=True,
                                default=generate_half_uuid,
                                validators=regexes.validator(
                                    'uuid', _('Invalid UUID.')))
    app_company_name = models.CharField(max_length=100, blank=True, null=True)
    app_short_name = models.CharField(max_length=100, blank=True, null=True)
    app_long_name = models.CharField(max_length=100, blank=True, null=True)
    app_version_label = models.CharField(max_length=40,
                                         blank=True,
                                         null=True,
                                         default='1.0',
                                         validators=[version_validator])
    app_is_watchface = models.BooleanField(default=False)
    app_is_hidden = models.BooleanField(default=False)
    app_is_shown_on_communication = models.BooleanField(default=False)
    app_capabilities = models.CharField(max_length=255, blank=True, null=True)
    app_keys = models.TextField(default="{}")
    app_jshint = models.BooleanField(default=True)
    app_platforms = models.TextField(max_length=255, blank=True, null=True)
    app_modern_multi_js = models.BooleanField(default=True)
    app_keywords = models.TextField(default='[]')

    app_capability_list = property(
        lambda self: self.app_capabilities.split(','))
    app_platform_list = property(lambda self: self.app_platforms.split(',')
                                 if self.app_platforms else [])

    OPTIMISATION_CHOICES = (
        ('0', 'None'),
        ('1', 'Limited'),
        ('s', 'Prefer smaller'),
        ('2', 'Prefer faster'),
        ('3', 'Aggressive (faster, bigger)'),
    )

    optimisation = models.CharField(max_length=1,
                                    choices=OPTIMISATION_CHOICES,
                                    default='s')

    github_repo = models.CharField(max_length=100, blank=True, null=True)
    github_branch = models.CharField(max_length=100, blank=True, null=True)
    github_last_sync = models.DateTimeField(blank=True, null=True)
    github_last_commit = models.CharField(max_length=40, blank=True, null=True)
    github_hook_uuid = models.CharField(max_length=36, blank=True, null=True)
    github_hook_build = models.BooleanField(default=False)

    def set_dependencies(self, dependencies):
        """ Set the project's dependencies from a dictionary.
        :param dependencies: A dictionary of dependency->version
        """
        with transaction.atomic():
            Dependency.objects.filter(project=self).delete()
            for name, version in dependencies.iteritems():
                dep = Dependency.objects.create(project=self,
                                                name=name,
                                                version=version)
                dep.save()

    @property
    def keywords(self):
        """ Get the project's keywords as a list of strings """
        return json.loads(self.app_keywords)

    @keywords.setter
    def keywords(self, value):
        """ Set the project's keywords from a list of strings """
        self.app_keywords = json.dumps(value)

    def get_dependencies(self):
        """ Get the project's dependencies as a dictionary
        :return: A dictinoary of dependency->version
        """
        return {d.name: d.version for d in self.dependencies.all()}

    @property
    def uses_array_message_keys(self):
        return isinstance(json.loads(self.app_keys), list)

    def get_parsed_appkeys(self):
        """ Get the project's app keys, or raise an error of any are invalid.
        :return: A list of (appkey, value) tuples, where value is either a length or a size, depending on the kind of appkey.
        """
        app_keys = json.loads(self.app_keys)
        if isinstance(app_keys, dict):
            return sorted(app_keys.iteritems(), key=lambda x: x[1])
        else:
            parsed_keys = []
            for appkey in app_keys:
                parsed = re.match(regexes.C_IDENTIFIER_WITH_INDEX, appkey)
                if not parsed:
                    raise ValueError("Bad Appkey %s" % appkey)
                parsed_keys.append((parsed.group(1), parsed.group(2) or 1))
            return parsed_keys

    def get_last_build(self):
        try:
            return self.builds.order_by('-id')[0]
        except IndexError:
            return None

    def get_menu_icon(self):
        try:
            return self.resources.filter(is_menu_icon=True)[0]
        except IndexError:
            return None

    def has_platform(self, platform):
        return self.app_platforms is None or platform in self.app_platform_list

    @property
    def semver(self):
        """ Get the app's version label formatted as a semver """
        return version_to_semver(self.app_version_label)

    @semver.setter
    def semver(self, value):
        """ Set the app's version label from a semver string"""
        self.app_version_label = semver_to_version(value)

    def clean(self):
        if isinstance(json.loads(self.app_keys),
                      list) and self.sdk_version == "2":
            raise ValidationError(
                _("SDK2 appKeys must be an object, not a list."))

    last_build = property(get_last_build)
    menu_icon = property(get_menu_icon)

    def __unicode__(self):
        return u"%s" % self.name
Exemple #5
0
class BuildResult(IdeModel):

    STATE_WAITING = 1
    STATE_FAILED = 2
    STATE_SUCCEEDED = 3
    STATE_CHOICES = ((STATE_WAITING, _('Pending')),
                     (STATE_FAILED, _('Failed')), (STATE_SUCCEEDED,
                                                   _('Succeeded')))

    DEBUG_INFO_MAP = {
        'aplite': ('debug_info.json', 'worker_debug_info.json'),
        'basalt': ('basalt_debug_info.json', 'basalt_worker_debug_info.json'),
        'chalk': ('chalk_debug_info.json', 'chalk_worker_debug_info.json'),
        'diorite':
        ('diorite_debug_info.json', 'diorite_worker_debug_info.json'),
        'emery': ('emery_debug_info.json', 'emery_worker_debug_info.json'),
    }
    DEBUG_APP = 0
    DEBUG_WORKER = 1

    project = models.ForeignKey(Project, related_name='builds')
    uuid = models.CharField(max_length=36,
                            default=make_uuid,
                            validators=regexes.validator(
                                'uuid', _('Invalid UUID.')))
    state = models.IntegerField(choices=STATE_CHOICES, default=STATE_WAITING)
    started = models.DateTimeField(auto_now_add=True, db_index=True)
    finished = models.DateTimeField(blank=True, null=True)

    def _get_dir(self):
        return '%s/' % self.uuid

    def get_url(self):
        return "%s%s/" % (settings.MEDIA_URL, self.uuid)

    @property
    def pbw(self):
        return '%sproject.pbw' % self._get_dir()

    @property
    def pbw_url(self):
        return '%sproject.pbw' % self.get_url()

    @property
    def package(self):
        return '%ssource.tar.gz' % self._get_dir()

    @property
    def package_url(self):
        return '%ssource.tar.gz' % self.get_url()

    @property
    def build_log(self):
        return '%scompile_log.txt' % self._get_dir()

    @property
    def build_log_url(self):
        return '%scompile_log.txt' % self.get_url()

    @property
    def simplyjs(self):
        return '%ssimply.js' % self._get_dir()

    def get_debug_info_filename(self, platform, kind):
        return self._get_dir() + self.DEBUG_INFO_MAP[platform][kind]

    def save_build_log(self, text):
        s3.save_file('builds',
                     self.build_log,
                     text,
                     public=True,
                     content_type='text/plain')

    def read_build_log(self):
        return s3.read_file('builds', self.build_log)

    def save_debug_info(self, json_info, platform, kind):
        text = json.dumps(json_info)
        s3.save_file('builds',
                     self.get_debug_info_filename(platform, kind),
                     text,
                     public=True,
                     content_type='application/json')

    def save_package(self, package_path):
        filename = '%s.tar.gz' % self.project.app_short_name.replace('/', '-')
        s3.upload_file('builds',
                       self.package,
                       package_path,
                       public=True,
                       download_filename=filename,
                       content_type='application/gzip')

    def save_pbw(self, pbw_path):
        s3.upload_file('builds',
                       self.pbw,
                       pbw_path,
                       public=True,
                       download_filename='%s.pbw' %
                       self.project.app_short_name.replace('/', '-'))

    def save_simplyjs(self, javascript):
        s3.save_file('builds',
                     self.simplyjs,
                     javascript,
                     public=True,
                     content_type='text/javascript')

    def get_sizes(self):
        sizes = {}
        for size in self.sizes.all():
            sizes[size.platform] = {
                'total': size.total_size,
                'app': size.binary_size,
                'resources': size.resource_size,
                'worker': size.worker_size,
            }
        return sizes
class PublishedMedia(IdeModel):
    project = models.ForeignKey('Project', related_name='published_media')
    name = models.CharField(max_length=100,
                            validators=regexes.validator(
                                'c_identifier', _('Invalid identifier')))
    media_id = models.IntegerField()
    glance = models.CharField(max_length=100,
                              blank=True,
                              validators=regexes.validator(
                                  'c_identifier', _('Invalid identifier')))
    has_timeline = models.BooleanField(default=False)
    timeline_tiny = models.CharField(max_length=100,
                                     blank=True,
                                     validators=regexes.validator(
                                         'c_identifier',
                                         _('Invalid identifier')))
    timeline_small = models.CharField(max_length=100,
                                      blank=True,
                                      validators=regexes.validator(
                                          'c_identifier',
                                          _('Invalid identifier')))
    timeline_large = models.CharField(max_length=100,
                                      blank=True,
                                      validators=regexes.validator(
                                          'c_identifier',
                                          _('Invalid identifier')))

    @classmethod
    def from_dict(cls, project, data):
        return cls.objects.create(project=project,
                                  name=data['name'],
                                  media_id=data.get('id', None),
                                  glance=data.get('glance', ''),
                                  has_timeline=('timeline' in data),
                                  timeline_tiny=data.get('timeline',
                                                         {}).get('tiny', ''),
                                  timeline_small=data.get('timeline',
                                                          {}).get('small', ''),
                                  timeline_large=data.get('timeline',
                                                          {}).get('large', ''))

    def to_dict(self):
        obj = {'name': self.name, 'id': self.media_id}
        if self.glance:
            obj['glance'] = self.glance
        if self.has_timeline:
            obj['timeline'] = {
                'tiny': self.timeline_tiny,
                'small': self.timeline_small,
                'large': self.timeline_large
            }
        return obj

    def clean(self):
        if self.has_timeline and self.glance and self.glance != self.timeline_tiny:
            raise ValidationError(
                _("If glance and timeline.tiny are both used, they must be identical."
                  ))
        if self.has_timeline and not (self.timeline_tiny
                                      and self.timeline_small
                                      and self.timeline_large):
            raise ValidationError(
                _("If timeline icons are enabled, they must all be set."))
        if not self.glance and not self.has_timeline:
            raise ValidationError(
                _("Glance and Timeline cannot both be unset."))
        if self.media_id < 0:
            raise ValidationError(_("Published Media IDs cannot be negative."))

    class Meta(IdeModel.Meta):
        unique_together = (('project', 'name'), ('project', 'media_id'))
Exemple #7
0
class SourceFile(TextFile):
    project = models.ForeignKey('Project', related_name='source_files')
    file_name = models.CharField(max_length=100,
                                 validators=regexes.validator(
                                     'source_file_name',
                                     _('Invalid file name.')))

    folder = 'sources'

    TARGETS = (
        ('app', _('App')),
        ('pkjs', _('PebbleKit JS')),
        ('worker', _('Worker')),
        ('public', _('Public Header File')),
        ('common', _('Shared JS')),
    )
    target = models.CharField(max_length=10, choices=TARGETS, default='app')

    DIR_MAP = {
        # Using an OrderedDict here ensures that 'src/' is checked last in get_details_for_path().
        'native':
        OrderedDict([
            ('pkjs', ['src/pkjs', 'src/js']),
            ('worker', ['worker_src/c', 'worker_src']),
            ('app', ['src/c', 'src']),
        ]),
        'pebblejs': {
            'app': ['src/js'],
        },
        'simplyjs': {
            'app': ['src'],
        },
        'rocky': {
            'app': ['src/rocky'],
            'pkjs': ['src/pkjs'],
            'common': ['src/common'],
        },
        'package': {
            'app': ['src/c'],
            'public': ['include'],
            'pkjs': ['src/js'],
        }
    }

    @classmethod
    def get_details_for_path(cls, project_type, path):
        """
        Given a project type and a path to a source file, determine what the file's target should be and
        what its name should be.
        """
        targets = cls.DIR_MAP[project_type]
        for target in targets:
            for base in targets[target]:
                base += '/'
                if path.startswith(base):
                    file_target = target
                    break
            else:
                continue
            break
        else:
            raise ValueError(
                _("Unacceptable file path for this project [%s]") % path)
        if file_target in ('pkjs',
                           'common') or project_type in ('pebblejs',
                                                         'simplyjs', 'rocky'):
            expected_exts = ('.js', '.json')
        else:
            expected_exts = ('.c', '.h')
        if not path.endswith(expected_exts):
            raise ValueError(
                _("Unacceptable file extension for %s file in [%s]. Expecting %s"
                  ) % (file_target, path, " or ".join(expected_exts)))
        return path[len(base):], file_target

    @property
    def project_path(self):
        return os.path.join(self.project_dir, self.file_name)

    @property
    def project_dir(self):
        try:
            return SourceFile.DIR_MAP[self.project.project_type][
                self.target][0]
        except KeyError:
            Exception("Invalid file type in project")

    class Meta(IdeModel.Meta):
        unique_together = (('project', 'file_name', 'target'), )
Exemple #8
0
class ResourceFile(IdeModel):
    project = models.ForeignKey('Project', related_name='resources')
    RESOURCE_KINDS = (
        ('raw', _('Binary blob')),
        ('bitmap', _('Bitmap Image')),
        ('png', _('1-bit PNG')),
        ('png-trans', _('1-bit PNG with transparency')),
        ('font', _('True-Type Font')),
        ('pbi', _('1-bit Pebble image')),
    )

    file_name = models.CharField(max_length=100,
                                 validators=regexes.validator(
                                     'resource_file_name',
                                     _("Invalid filename.")))
    kind = models.CharField(max_length=9, choices=RESOURCE_KINDS)
    is_menu_icon = models.BooleanField(default=False)

    def get_best_variant(self, tags_string):
        try:
            return self.variants.get(tags=tags_string)
        except ResourceVariant.DoesNotExist:
            return self.get_default_variant()

    def rename(self, new_name):
        if os.path.splitext(
                self.file_name)[1] != os.path.splitext(new_name)[1]:
            raise ValidationError(
                _("Cannot change file type when renaming resource"))
        self.file_name = new_name

    def get_default_variant(self):
        return self.variants.get(tags="")

    def get_identifiers(self):
        return ResourceIdentifier.objects.filter(resource_file=self)

    def copy_all_variants_to_dir(self, path):
        filename_parts = os.path.splitext(self.file_name)
        for variant in self.variants.all():
            abs_target = "%s/%s%s%s" % (path, filename_parts[0],
                                        variant.get_tags_string(),
                                        filename_parts[1])
            if not abs_target.startswith(path):
                raise Exception(_("Suspicious filename: %s") % self.file_name)
            variant.copy_to_path(abs_target)

    def save(self, *args, **kwargs):
        self.clean_fields()
        self.project.last_modified = now()
        self.project.save()
        super(ResourceFile, self).save(*args, **kwargs)

    DIR_MAP = {
        'pbi': 'images',
        'png': 'images',
        'png-trans': 'images',
        'bitmap': 'images',
        'font': 'fonts',
        'raw': 'data'
    }

    def get_path(self, variant):
        return self.get_best_variant(variant).get_path()

    @property
    def root_path(self):
        # Try to get the default variant and return its path
        try:
            return self.get_default_variant().root_path
        except ResourceVariant.DoesNotExist:
            # Failing that, strip the suffixes off an existing one
            return self.variants.all()[0].root_path

    class Meta(IdeModel.Meta):
        unique_together = (('project', 'file_name'), )
Exemple #9
0
class ResourceIdentifier(IdeModel):
    resource_file = models.ForeignKey(ResourceFile, related_name='identifiers')
    resource_id = models.CharField(max_length=100,
                                   validators=regexes.validator(
                                       'c_identifier',
                                       _("Invalid resource ID.")))
    character_regex = models.CharField(max_length=100, blank=True, null=True)
    tracking = models.IntegerField(blank=True, null=True)
    compatibility = models.CharField(max_length=10, blank=True, null=True)
    target_platforms = models.CharField(max_length=100,
                                        null=True,
                                        blank=True,
                                        default=None)

    MEMORY_FORMATS = (
        ('Smallest', _('Smallest')),
        ('SmallestPalette', _('Smallest Palette')),
        ('1Bit', _('1-bit')),
        ('8Bit', _('8-bit')),
        ('1BitPalette', _('1-bit Palette')),
        ('2BitPalette', _('2-bit Palette')),
        ('4BitPalette', _('4-bit Palette')),
    )
    memory_format = models.CharField(max_length=15,
                                     choices=MEMORY_FORMATS,
                                     null=True,
                                     blank=True)

    STORAGE_FORMATS = (('pbi', _('1 bit Pebble Image')), ('png', _('PNG')))
    storage_format = models.CharField(max_length=3,
                                      choices=STORAGE_FORMATS,
                                      null=True,
                                      blank=True)

    SPACE_OPTIMISATIONS = (('storage', _('Storage')), ('memory', _('Memory')))
    space_optimisation = models.CharField(max_length=7,
                                          choices=SPACE_OPTIMISATIONS,
                                          null=True,
                                          blank=True)

    def get_options_dict(self, with_id=False):
        """ Return the ResourceIdentifier's options as a dictionary. Optionally include its ID in the key 'id' """
        d = {
            # Resource ID
            'target_platforms':
            json.loads(self.target_platforms)
            if self.target_platforms else None,

            # Font options
            'regex':
            self.character_regex,
            'tracking':
            self.tracking,
            'compatibility':
            self.compatibility,

            # Bitmap options
            'memory_format':
            self.memory_format,
            'storage_format':
            self.storage_format,
            'space_optimisation':
            self.space_optimisation
        }
        if with_id:
            d['id'] = self.resource_id
        return d

    def save(self, *args, **kwargs):
        self.resource_file.project.last_modified = now()
        self.resource_file.project.save()
        super(ResourceIdentifier, self).save(*args, **kwargs)