mirror of
				https://github.com/bspeice/Melodia
				synced 2025-10-31 01:10:39 -04:00 
			
		
		
		
	Upload the initial documentation for the archiver application
This commit is contained in:
		
							
								
								
									
										56
									
								
								archiver/listfield.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								archiver/listfield.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,56 @@ | ||||
| ''' | ||||
| Testing documentation | ||||
| ''' | ||||
| from django.db import models | ||||
| import re, itertools | ||||
|  | ||||
| class IntegerListField(models.TextField): | ||||
| 	""" | ||||
| 	Store a list of integers in a database string. | ||||
| 	Format is:  | ||||
| 	[<int_1>, <int_2>, <int_3>, ... , <int_n>] | ||||
| 	""" | ||||
|  | ||||
| 	description = "Field type for storing lists of integers." | ||||
|  | ||||
| 	__metaclass__ = models.SubfieldBase | ||||
|  | ||||
| 	def __init__(self, *args, **kwargs): | ||||
| 		super(IntegerListField, self).__init__(*args, **kwargs) | ||||
|  | ||||
|  | ||||
| 	#Convert database to python | ||||
| 	def to_python(self, value): | ||||
| 		if isinstance(value, list): | ||||
| 			return value | ||||
|  | ||||
| 		#Process a database string | ||||
| 		 | ||||
| 		#Validation first | ||||
| 		if len(value) <= 0: | ||||
| 			return [] | ||||
|  | ||||
| 		if value[0] != '[' or value[-1] != ']': | ||||
| 			raise ValidationError("Invalid input to parse a list of integers!") | ||||
|  | ||||
| 		#Note that any non-digit string is a valid separator | ||||
| 		_csv_regex = "[0-9]" | ||||
| 		csv_regex  = re.compile(_csv_regex) | ||||
|  | ||||
| 		#Synonymous to: | ||||
| 		#string_list = filter(None, csv_regex.findall(value)) | ||||
| 		string_list  = itertools.ifilter(None, csv_regex.findall(value)) | ||||
| 		value_list   = [int(i) for i in string_list] | ||||
|  | ||||
| 		return value_list | ||||
|  | ||||
| 	#Convert python to database | ||||
| 	def get_prep_value(self, value): | ||||
| 		if not isinstance(value, list): | ||||
| 			raise ValidationError("Invalid list given to put in database!") | ||||
|  | ||||
| 		separator_string = ", " | ||||
|  | ||||
| 		list_elements = separator_string.join(map(str, value)) | ||||
|  | ||||
| 		return "[" + list_elements + "]" | ||||
| @ -1,10 +1,3 @@ | ||||
| ''' | ||||
| .. currentmodule:: archiver.models | ||||
|  | ||||
| I'm trying to link to :class:`~archiver.models.archive.Archive`! | ||||
|  | ||||
| ''' | ||||
|  | ||||
| # Create your models here. | ||||
| from archive import Archive | ||||
| from song import Song | ||||
|  | ||||
| @ -1,6 +1,4 @@ | ||||
| """ | ||||
| .. module:: archiver.models.archive | ||||
|  | ||||
| This is the Archive model for the backend of Melodia. It's functionality is to | ||||
| provide a grouping of songs based on where they are located in the filesystem. | ||||
| It controls the high-level functionality of managing multiple archives | ||||
|  | ||||
| @ -113,5 +113,8 @@ class Feed(models.Model): | ||||
| 		:param dry_run: Calculate what would have been downloaded or deleted, but do not actually do either. | ||||
| 		:param forbid_delete: Run, and only download new episodes. Ignores the :data:`max_episodes` field for this podcast. | ||||
|  | ||||
| 		.. todo:: | ||||
| 		   Actually write this method... | ||||
|  | ||||
|         """ | ||||
|         pass | ||||
|  | ||||
| @ -1,24 +1,17 @@ | ||||
| """ | ||||
| .. module:: archiver.models | ||||
|  | ||||
| Playlist model | ||||
| Each playlist is a high-level ordering of songs. There really isn't much to a playlist - just its name, and the songs inside it. | ||||
| However, we need to have a way to guarantee song order, in addition to re-ordering. A ManyToMany field can't do this. | ||||
| As such, a custom IntegerListField is implemented - it takes a python list of ints, converts it to a text field in the DB, | ||||
| and then back to a python list. This way, we can guarantee order, and have a song appear multiple times. | ||||
| The IntegerListField itself uses the ID of each song as the int in a list. For example, a list of: | ||||
|  | ||||
|    [1, 3, 5, 17] | ||||
|  | ||||
| Means that the playlist is made up of four songs. The order of the playlist is the song with index 1, 3, 5, and 17. | ||||
| Additionally, the ManyToMany field is included to make sure we don't use the global Songs manager - it just seems hackish. | ||||
| The Playlist model is simply that - it's a playlist of songs. However, we do | ||||
| have to guarantee the song order, in addition to re-ordering the playlist. | ||||
| As such, a :class:`models.ManyToManyField` isn't sufficient. We use a custom | ||||
| database field to store a list of integers - the :class:`IntegerListField`. | ||||
| This way we can guarantee song order, re-order the playlist, have songs | ||||
| appear multiple times, etc. | ||||
| """ | ||||
|  | ||||
| from django.db import models | ||||
| from django.core.exceptions import ObjectDoesNotExist | ||||
|  | ||||
| from song import Song | ||||
| from listfield import IntegerListField | ||||
| from archiver.listfield import IntegerListField | ||||
|  | ||||
| import re | ||||
| from warnings import warn | ||||
| @ -28,83 +21,74 @@ class Playlist(models.Model): | ||||
| 		app_label = 'archiver' | ||||
|  | ||||
| 	""" | ||||
| 	The Playlist class defines the playlist model and its operations. | ||||
| 	Currently supported are add, move, and remove operations, as well as exporting to | ||||
| 	multiple formats. | ||||
| 	.. data:: name | ||||
|  | ||||
| 	   String with the human-readable name for this playlist. | ||||
|  | ||||
| 	.. data:: song_list | ||||
|  | ||||
| 	   List made up of Python integers. Each integer is assumed | ||||
| 	   to be a primary key to the :data:`Song.id` field for a song. | ||||
| 	""" | ||||
|  | ||||
| 	name       = models.CharField(max_length = 255) | ||||
| 	song_list = IntegerListField() | ||||
|  | ||||
| 	#This is a bit of a backup field, since technically the song PK's are in | ||||
| 	#the song_order field. However, it might be useful to just get a reference | ||||
| 	#to the songs in this playlist. Additionally, it's kind of hackish to reference | ||||
| 	#the global Song manager, rather than something that is specific for this playlist. | ||||
| 	songs      = models.ManyToManyField(Song) | ||||
|  | ||||
| 	def _populate_songs(self): | ||||
| 		""" | ||||
| 		Make sure that the 'songs' relation is up-to-date. | ||||
| 		""" | ||||
| 		#This operation works by getting the ID's for all songs currently in the playlist, | ||||
| 		#calculates what we need to add, what we need to remove, and then does it. | ||||
| 		#As much work as is possible is done on the python side to avoid the DB at all costs. | ||||
| 		current_song_ids     = [song.id for song in self.songs.all()] | ||||
| 		current_song_ids_set = set(current_song_ids) | ||||
|  | ||||
| 		new_song_ids_set = set(self.song_list) | ||||
|  | ||||
| 		remove_set = current_song_ids_set.difference(new_song_ids_set) | ||||
| 		add_set    = new_song_ids_set.difference(current_song_ids_set) | ||||
|  | ||||
| 		for song_id in remove_set: | ||||
| 			song = self.songs.get(id = song_id) | ||||
| 			song.remove() | ||||
|  | ||||
| 		for song_id in add_set: | ||||
| 			song = Song.objects.get(id = song_id) #Using the global Songs manager is unavoidable for this one | ||||
| 			self.songs.add(song) | ||||
|  | ||||
| 	def insert(self, position, new_song): | ||||
| 		""" | ||||
| 		Insert a new song into the playlist at a specific position. | ||||
|  | ||||
| 		:param position: **Index** for the position this new song should be inserted at. | ||||
| 		:param new_song: Reference to a :class:`Song` instance that will be inserted. | ||||
| 		""" | ||||
|  | ||||
| 		if not isinstance(new_song, Song): | ||||
| 			#Not given a song reference, raise an error | ||||
| 			raise ValidationError("Not given a song reference to insert.") | ||||
| 			 | ||||
| 		self.songs.add(new_song) | ||||
|  | ||||
| 		self.song_list.insert(position, new_song.id) | ||||
|  | ||||
| 		self._populate_songs() | ||||
|  | ||||
| 	def append(self, new_song): | ||||
| 		""" | ||||
| 		Add a new song to the end of the playlist. | ||||
|  | ||||
| 		:param new_song: Reference to a :class:`Song` instance to be appended. | ||||
| 		""" | ||||
|  | ||||
| 		if not isinstance(new_song, Song): | ||||
| 			#Not given a song reference, raise an error | ||||
| 			raise ValidationError("Not given a song reference to insert.") | ||||
|  | ||||
| 		self.songs.add(new_song) | ||||
|  | ||||
| 		self.song_list.append(new_song.id) | ||||
|  | ||||
| 		self._populate_songs() | ||||
|  | ||||
| 	def move(self, original_position, new_position): | ||||
| 		""" | ||||
| 		Move a song from one position to another | ||||
|  | ||||
| 		:param original_position: The index of the song we want to move | ||||
| 		:param new_position: The index of where the song should be. See note below. | ||||
|  | ||||
| 		.. note:: | ||||
|  | ||||
| 		   When moving songs, it's a bit weird since the index we're actually | ||||
| 		   moving to may change. Consider the scenario -- | ||||
|  | ||||
| 		      * Function called with indexes 4 and 6 | ||||
|  | ||||
| 		      * Song removed from index 4  | ||||
|  | ||||
| 		        * The song that was in index 6 is now at index 5 | ||||
|  | ||||
| 		      * Song inserted at index 6 in new list - one further than originally intended. | ||||
|  | ||||
| 		   As such, the behavior is that the song at index ``original_position`` is placed | ||||
| 		   above the song at ``new_position`` when this function is called. | ||||
| 		""" | ||||
| 		if original_position == new_position: | ||||
| 			return | ||||
|  | ||||
| 		song_id = self.song_list[original_position] | ||||
|  | ||||
| 		#This is actually a bit more complicated than it first appears, since the index we're moving to may actually change | ||||
| 		#when we remove an item. | ||||
| 		if new_position < original_position: | ||||
| 			del self.song_list[original_position] | ||||
| 			self.song_list.insert(new_position, song_id) | ||||
| @ -113,25 +97,29 @@ class Playlist(models.Model): | ||||
| 			del self.song_list[original_position] | ||||
| 			self.song_list.insert(new_position - 1, song_id) #Account for the list indices shifting down. | ||||
|  | ||||
| 		self._populate_songs() | ||||
|  | ||||
| 	def remove(self, position): | ||||
| 		""" | ||||
| 		Remove a song from this playlist. | ||||
|  | ||||
| 		:param position: Index of the song to be removed | ||||
| 		""" | ||||
| 		if position > len(self.song_list): | ||||
| 			return False | ||||
|  | ||||
| 		del self.song_list[position] | ||||
|  | ||||
| 		self._populate_songs() | ||||
|  | ||||
| 	def export(self, playlist_type = "m3u"): | ||||
| 		""" | ||||
| 		Export this internal playlist to a file format. | ||||
| 		Supported formats: | ||||
| 			-pls | ||||
| 			-m3u | ||||
| 		Return value is a string containing the playlist - | ||||
| 		you can write it to file as you deem necessary. | ||||
|  | ||||
| 		   * pls | ||||
| 		   * m3u | ||||
|  | ||||
| 		:param playlist_type: String containing the file type to export to | ||||
| 		:rtype: String containing the file content for this playlist. | ||||
| 		""" | ||||
|  | ||||
| 		if playlist_type == "pls": | ||||
| 			#Playlist header | ||||
| 			playlist_string = "[playlist]" | ||||
| @ -162,13 +150,26 @@ class Playlist(models.Model): | ||||
|  | ||||
| 			return playlist_string | ||||
|  | ||||
| 	def _import(self, playlist_string = None): | ||||
| 	def playlist_import(self, playlist_string = None): | ||||
| 		""" | ||||
| 		Import and convert a playlist into native DB format. | ||||
| 		This function will return true if the playlist format was recognized, false otherwise. | ||||
| 		It will return true even if there are errors processing individual songs in the playlist. | ||||
| 		As a side note - the _import() name is used since python doesn't let | ||||
| 		you name a function import(). | ||||
|  | ||||
| 		:param playlist_string: A string with the file content we're trying to import. | ||||
|  | ||||
| 		:rtype: Returns true of the playlist format was recognized. See notes on processing below. | ||||
|  | ||||
| 		.. warning:: | ||||
|  | ||||
| 		   The semantics on returning are nitpicky. This function will return ``False`` if the | ||||
| 		   playlist format was not recognized. If there are errors in processing, this | ||||
| 		   function will still return ``True``. | ||||
|  | ||||
| 		   For example, if you try to import a song which does not exist in an :class:`Archive`, | ||||
| 		   it will fail that song silently. | ||||
|  | ||||
| 		.. todo:: | ||||
|  | ||||
| 		   Actually write the import code. | ||||
| 		""" | ||||
| 		#TODO: Code playlist importing | ||||
| 		self.song_list = [] | ||||
|  | ||||
| @ -1,3 +1,9 @@ | ||||
| """ | ||||
| The :class:`Song` model is by far the most complicated and involved model. | ||||
| Each instance is a single music file. This model is used to store metadata | ||||
| about the song. | ||||
| """ | ||||
|  | ||||
| from django.db import models | ||||
| from Melodia import melodia_settings | ||||
|  | ||||
| @ -5,12 +11,6 @@ from archive import Archive | ||||
|  | ||||
| import datetime | ||||
| import os.path | ||||
| """ | ||||
| The Song model | ||||
| Each instance of a Song represents a single music file. | ||||
| This database model is used for storing the metadata information about a song, | ||||
| and helps in doing sorting etc. | ||||
| """ | ||||
|  | ||||
| _default_string = "_UNAVAILABLE_" | ||||
| _default_date   = datetime.datetime.now | ||||
| @ -32,18 +32,119 @@ _default_rating_choices = ( | ||||
| 		) | ||||
|  | ||||
| class Song (models.Model): | ||||
| 	class Meta: | ||||
| 		app_label = 'archiver' | ||||
| 	 | ||||
| 	""" | ||||
| 	This class defines the fields and functions related to controlling | ||||
| 	individual music files. | ||||
| 	Note that the Playlist model depends on this model's PK being an int. | ||||
| 	.. data:: title | ||||
| 	    | ||||
| 	   Title tag of this song | ||||
|  | ||||
| 	.. data:: artist | ||||
|  | ||||
| 	   Artist tag of this song. | ||||
|  | ||||
| 	.. data:: album_artist | ||||
|  | ||||
| 	   Album artist tag of this song. Can be used to group albums where | ||||
| 	   individual songs were made by different people. | ||||
|  | ||||
| 	.. data:: album | ||||
|  | ||||
| 	   Album tag of this song. | ||||
|  | ||||
| 	.. data:: year | ||||
| 	 | ||||
| 	   Integer representing the year this song was made. | ||||
|  | ||||
| 	.. data:: genre | ||||
|  | ||||
| 	   Genre tag of this song. This is a general :class:`models.CharField` | ||||
| 	   field, and is not limited to a specific set of genres. | ||||
|  | ||||
| 	.. data:: bpm | ||||
|  | ||||
| 	   Beats per minute of this song (integer). | ||||
|  | ||||
| 	.. data:: disc_number | ||||
|  | ||||
| 	   Disc number this song came from | ||||
|  | ||||
| 	.. data:: disc_total | ||||
|  | ||||
| 	   Total number of discs in the album this song is from | ||||
|  | ||||
| 	.. data:: track_number | ||||
|  | ||||
| 	   Track number in the album this song is from | ||||
|  | ||||
| 	.. data:: track_total | ||||
|  | ||||
| 	   Total number of tracks in the album this song is from | ||||
|  | ||||
| 	.. data:: comment | ||||
|  | ||||
| 	   Comment tag of this song | ||||
|  | ||||
| 	.. data:: bit_rate | ||||
|  | ||||
| 	   Integer representing the bit rate of this song | ||||
|  | ||||
| 	.. data:: duration | ||||
| 	 | ||||
| 	   Duration (in seconds, floating-point value) of this song | ||||
|  | ||||
| 	.. data:: add_date | ||||
|  | ||||
| 	   Date (not time) this song was added to the DB. Should **not** be | ||||
| 	   modified outside of this class' methods. | ||||
|  | ||||
| 	.. data:: url | ||||
|  | ||||
| 	   URL for where this file is located on disk. | ||||
|  | ||||
| 	.. data:: file_hash | ||||
|  | ||||
| 	   The hash string for this file - used to quickly check if the file has | ||||
| 	   been modified. | ||||
|  | ||||
| 	.. data:: file_size | ||||
|  | ||||
| 	   Size of the file in bytes. | ||||
|  | ||||
| 	.. data:: play_count | ||||
|  | ||||
| 	   How many times this file has been played through (defined as greater | ||||
| 	   than 50% of the song heard before skipping) | ||||
|  | ||||
| 	.. data:: skip_count | ||||
|  | ||||
| 	   How many times this file has been skipped (defined as less than 50% of | ||||
| 	   the song heard before skipping) | ||||
|  | ||||
| 	.. data:: rating | ||||
|  | ||||
| 	   Rating for this song. Ratings are as follows in order of increasing favoredness  | ||||
| 	   on a 1--5 scale -- | ||||
|  | ||||
| 	   =========    ========    ================ | ||||
| 	   Rating:      Value:      Class field: | ||||
| 	   =========    ========    ================ | ||||
| 	   Default      0           RATING_DEFAULT | ||||
| 	   Bad          1           RATING_BAD | ||||
| 	   OK           2           RATING_OK | ||||
| 	   Decent       3           RATING_DECENT | ||||
| 	   Good         4           RATING_GOOD | ||||
| 	   Excellent    5           RATING_EXCELLENT | ||||
| 	   =========    ========    ================ | ||||
|  | ||||
| 	.. todo::  | ||||
|  | ||||
| 	   Change defaults to allow for ``None`` instead | ||||
| 	   Change private functions to public as need be | ||||
| 	""" | ||||
|  | ||||
| 	#Standard song metadata | ||||
| 	title        = models.CharField(max_length = 64, default = _default_string) | ||||
| 	artist       = models.CharField(max_length = 64, default = _default_string) | ||||
| 	album_artist = models.CharField(max_length = 64, default = _default_string) | ||||
| 	album        = models.CharField(max_length = 64, default = _default_string) | ||||
| 	year         = models.IntegerField(default = _default_string) | ||||
| 	genre        = models.CharField(max_length = 64, default = _default_string) | ||||
| @ -58,7 +159,6 @@ class Song (models.Model): | ||||
| 	bit_rate         = models.IntegerField(default = _default_int) | ||||
| 	duration         = models.FloatField(default = _default_int) | ||||
| 	add_date         = models.DateField(default = _default_date) | ||||
| 	echonest_song_id = models.CharField(max_length = 64, default = _default_string) | ||||
| 	url              = models.CharField(max_length = 255) | ||||
| 	file_hash        = melodia_settings.HASH_RESULT_DB_TYPE | ||||
| 	file_size        = models.IntegerField(default = _default_int) | ||||
| @ -79,6 +179,9 @@ class Song (models.Model): | ||||
| 	RATING_GOOD      = _default_rating_good | ||||
| 	RATING_EXCELLENT = _default_rating_excellent | ||||
|  | ||||
| 	class Meta: | ||||
| 		app_label = 'archiver' | ||||
|  | ||||
| 	def _get_full_url(self): | ||||
| 		"Combine this song's URL with the URL of its parent" | ||||
| 		return os.path.join(parent_archive.root_folder, self.url) | ||||
| @ -111,10 +214,6 @@ class Song (models.Model): | ||||
| 		self.file_hash = hash(file_handle.read()) | ||||
| 		self.file_size = os.stat(self._get_full_url).st_size | ||||
|  | ||||
| 	def _grab_metadata_echonest(self): | ||||
| 		"Populate this song's metadata using EchoNest" | ||||
| 		pass | ||||
|  | ||||
| 	def _grab_metadata_local(self): | ||||
| 		"Populate this song's metadata using what is locally available" | ||||
| 		#Use mutagen to get the song metadata | ||||
| @ -145,8 +244,10 @@ class Song (models.Model): | ||||
| 			#Couldn't grab the local data | ||||
| 			return False | ||||
|  | ||||
| 	def populate_metadata(self, use_echonest = False): | ||||
| 		"Populate the metadata of this song (only if file hash has changed), and save the result." | ||||
| 	def populate_metadata(self): | ||||
| 		""" | ||||
| 		Populate the metadata of this song (only if file hash has changed), and save the result. | ||||
| 		""" | ||||
| 		if self._file_not_changed(): | ||||
| 			return | ||||
|  | ||||
| @ -157,6 +258,15 @@ class Song (models.Model): | ||||
| 		else: | ||||
| 			self._grab_metadata_local() | ||||
| 			 | ||||
| 	def convert(self, output_location, output_format, progress_func = lambda x, y: None): | ||||
| 		"Convert a song to a new format." | ||||
| 	def convert(self, output_location, output_format): | ||||
| 		""" | ||||
| 		Convert a song to a new format. | ||||
|  | ||||
| 		:param output_location: String URL of where the resulting file should be stored | ||||
| 		:param output_format: Output format of the resulting file | ||||
|  | ||||
| 		.. todo:: | ||||
|  | ||||
| 		   Actually write the code to convert files, or abandon if necessary | ||||
| 		""" | ||||
| 		pass #Need to get pydub code in place | ||||
|  | ||||
| @ -26,7 +26,13 @@ sys.path.insert(0, os.path.abspath('../..')) # Django project root | ||||
|  | ||||
| # Add any Sphinx extension module names here, as strings. They can be extensions | ||||
| # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. | ||||
| extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] | ||||
| extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.viewcode', 'sphinx.ext.todo'] | ||||
|  | ||||
| # Enable TODO support | ||||
| todo_include_todos = True | ||||
|  | ||||
| # Document class members in source order | ||||
| autodoc_member_order = 'bysource' | ||||
|  | ||||
| # Add any paths that contain templates here, relative to this directory. | ||||
| templates_path = ['_templates'] | ||||
|  | ||||
							
								
								
									
										39
									
								
								doc/docs/archiver.models.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								doc/docs/archiver.models.rst
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | ||||
| models Package | ||||
| ============== | ||||
|  | ||||
| :mod:`models` Package | ||||
| --------------------- | ||||
|  | ||||
| .. automodule:: archiver.models | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| :mod:`archive` Module | ||||
| --------------------- | ||||
|  | ||||
| .. automodule:: archiver.models.archive | ||||
|     :members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| :mod:`feed` Module | ||||
| ------------------ | ||||
|  | ||||
| .. automodule:: archiver.models.feed | ||||
|     :members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| :mod:`playlist` Module | ||||
| ---------------------- | ||||
|  | ||||
| .. automodule:: archiver.models.playlist | ||||
|     :members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| :mod:`song` Module | ||||
| ------------------ | ||||
|  | ||||
| .. automodule:: archiver.models.song | ||||
|     :members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| @ -1,5 +1,42 @@ | ||||
| ==== | ||||
| Archive backend documentation | ||||
| ==== | ||||
| archiver Package | ||||
| ================ | ||||
|  | ||||
| :mod:`archiver` Package | ||||
| ----------------------- | ||||
|  | ||||
| .. automodule:: archiver.__init__ | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| :mod:`listfield` Module | ||||
| ----------------------- | ||||
|  | ||||
| .. automodule:: archiver.listfield | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| :mod:`tests` Module | ||||
| ------------------- | ||||
|  | ||||
| .. automodule:: archiver.tests | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| :mod:`views` Module | ||||
| ------------------- | ||||
|  | ||||
| .. automodule:: archiver.views | ||||
|     :members: | ||||
|     :undoc-members: | ||||
|     :show-inheritance: | ||||
|  | ||||
| Subpackages | ||||
| ----------- | ||||
|  | ||||
| .. toctree:: | ||||
|  | ||||
|     archiver.models | ||||
|  | ||||
| .. automodule:: archiver | ||||
|  | ||||
							
								
								
									
										7
									
								
								doc/docs/modules.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								doc/docs/modules.rst
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | ||||
| archiver | ||||
| ======== | ||||
|  | ||||
| .. toctree:: | ||||
|    :maxdepth: 4 | ||||
|  | ||||
|    archiver | ||||
		Reference in New Issue
	
	Block a user
	 Bradlee Speice
					Bradlee Speice