1. AutorizeUser(string Login, string Password) : User - возвращает информацию о пользователе по логину и паролю.
2. GetCorpora(int UserId) : Corpus[] - возвращает список всех корпусов пользователя.
3. GetDocuments(int CorpusId) : Document[] - возвращает список всех документов в корпусе.
4. GetDocument(int Document) : Document - возвращает информацию о документе.
5. MakeCorpus(string Name, Document[] documents) : int - создает общий корпус с выбранными документами (можно создать и пустой корпус, передав пустой массив).
6. MakeCorpus(string Name, Document[] documents, int UserId) : int - создает частный корпус с выбранными документами (можно создать и пустой корпус, передав пустой массив).
7. AddUser(User user) : int - регистрация нового пользователя.
8. UpdateUser(User user) : void - изменение информации о пользователе. По старому идентификатору обновляются все поля.
9. DeleteUser(int userId) : void - удаление информации о пользователе по идентификатору. При этом происходит удаление всех корпусов пользователя.
10. AddDocument(Document document) : int - добавление нового документа.
11. UpdateDocument(Document document) : void - изменение документа. По старому идентификатору обновляются все поля.
12. DeleteDocument(int documentId) : void - удаление документа по идентификатору.
13. UpdateCorpus(Corpus corpus) : void - изменение корпуса. По старому идентификатору обновляются все поля.
14. DeleteCorpus(int corpusId) : void - удаление корпуса по идентификатору. При этом происходит удаление всех документов корпуса.
15. FindDocuments(string Name, int corpusId) : Document[] - поиск документа по имени.
16. FindText(string Text) : Document[] - поиск фрагмента по документам.
4. Входные и выходные данные
Источниками входных данных являются другие сервисы, реализованные в программной системе. Выходные данные должны посылаться так же в другие сервисы согласно стандартам взаимодействия, реализованные в программной системе.
Стандартами взаимодействия являются вызов методов сервиса, а также использование классов, определенных в контракте сервиса:
Были выделены следующие сущности и атрибуты:
1. User - пользователь с информацией о ФИО (nullable), электронной почтой (not nullable), логине (not nullable) и пароле (not nullable) пользователя.
2. Corpus - корпус с информацией о названии корпуса (not nullable).
3. Document - документ, содержащий оригинальный файл (nullable), плоский текст (not nullable), аннотацию (nullable), строку HTML-разметки (nullable) и название (not nullable).
5. Сообщения
При работе с сервисом в случае успешной работы метод возвращает данные в соответствии с сигнатурой метода, например:
1. При вызове метода FindDocuments вернется массив документов.
2. При вызове метода DeleteCorpus нет возвращаемого значения.
Возможные виды специфичных ошибок при работе с сервисом представлены в таблице C.1. Остальные возможные виды выдаваемых ошибок связаны с WCF-сервисами и используемыми базами данных, сведенья о них можно найти в соответствующей документации.
Таблица C.1. Сообщения специфичных ошибок
|
Класс ошибки |
Сообщение ошибки |
Описание ошибки |
Требуемые действия при возникновении ошибки. |
|
|
Document |
Такого документа не существует |
Метод UpdateDocument. Документа с данным идентификатором нет в базе данных. |
Изменить идентификатор документа или добавить данный документ в базу. |
|
|
Corpus |
Такого корпуса не существует |
Метод UpdateCorpus. Корпуса с данным идентификатором нет в базе данных. |
Изменить идентификатор корпуса или добавить данный корпус в базу. |
|
|
User |
Другой пользователь с таким логином уже существует |
Метод UpdateUser. В базе данных существует другой пользователь с таким логином |
Изменить новый логин изменяемого пользователя. |
|
|
User |
Такого пользователя не существует |
Метод UpdateUser. Пользователя с данным идентификатором нет в базе данных. |
Изменить идентификатор пользователя или добавить данного пользователя в базу. |
|
|
User |
Пользователь с таким логином уже существует |
Метод AddUser. В базе данных существует пользователь с таким логином |
Изменить логин добавляемого пользователя. |
Приложение Г
Листинг
namespace CorporaStorageServiseContract
{
[ServiceContract]
public interface ICorporaStorageService
{
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "AutorizeUser?Login={Login}&Password={Password}")]
User AutorizeUser(string Login, string Password);
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "GetCorpora?UserId={UserId}")]
Corpus[] GetCorpora(int UserId);
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "GetDocuments?CorpusId={CorpusId}")]
Document[] GetDocuments(int CorpusId);
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "GetDocument?DocumentId={DocumentId}")]
Document GetDocument(int DocumentId);
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "MakeCommonCorpus")]
int MakeCommonCorpus(string Name, Document[] documents);
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "MakeCorpus")]
int MakeCorpus(string Name, Document[] documents, int UserId);
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "AddUser")]
int AddUser(User user);
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "UpdateUser")]
void UpdateUser(User user);
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "DeleteUser?userId={userId}")]
void DeleteUser(int userId);
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "AddDocument")]
int AddDocument(Document document);
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "UpdateDocument")]
void UpdateDocument(Document document, bool IsTextChanged);
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "DeleteDocument?id={documentId}")]
void DeleteDocument(int documentId);
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "UpdateCorpus")]
void UpdateCorpus(Corpus corpus);
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json, UriTemplate = "DeleteCorpus?id={corpusId}")]
void DeleteCorpus(int corpusId);
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "FindDocuments")]
Document[] FindDocuments(string Name, int corpusId);
[OperationContract]
[WebInvoke(Method = "POST",
BodyStyle = WebMessageBodyStyle.Wrapped,
ResponseFormat = WebMessageFormat.Json,
UriTemplate = "FindText")]
int[] FindText(string Text, int corpusId);
}
}
namespace PaperCatPortal
{
public class CorporaStorageService : ICorporaStorageService
{
static HashSet<string> stopwords = new HashSet<string>() { "a", "about", "above", "after", "again", "against", "all", "am", "an", "and", "any", "are", "aren't", "as", "at", "be", "because", "been", "before", "being", "below", "between", "both", "but", "by", "can't", "cannot", "could", "couldn't", "did", "didn't", "do", "does", "doesn't", "doing", "don't", "down", "during", "each", "few", "for", "from", "further", "had", "hadn't", "has", "hasn't", "have", "haven't", "having", "he", "he'd", "he'll", "he's", "her", "here", "here's", "hers", "herself", "him", "himself", "his", "how", "how's", "i", "i'd", "i'll", "i'm", "i've", "if", "in", "into", "is", "isn't", "it", "it's", "its", "itself", "let's", "me", "more", "most", "mustn't", "my", "myself", "no", "nor", "not", "of", "off", "on", "once", "only", "or", "other", "ought", "our", "ours”, “ourselves", "out", "over", "own", "same", "shan't", "she", "she'd", "she'll", "she's", "should", "shouldn't", "so", "some", "such", "than", "that", "that's", "the", "their", "theirs", "them", "themselves", "then", "there", "there's", "these", "they", "they'd", "they'll", "they're", "they've", "this", "those", "through", "to", "too", "under", "until", "up", "very", "was", "wasn't", "we", "we'd", "we'll", "we're", "we've", "were", "weren't", "what", "what's", "when", "when's", "where", "where's", "which", "while", "who", "who's", "whom", "why", "why's", "with", "won't", "would", "wouldn't", "you", "you'd", "you'll", "you're", "you've", "your", "yours", "yourself", "yourselves" };
public int AddDocument(Document document)
{
if (document == null)
throw new Exception("Параметр не может быть null");
using (var context = new PaperCatModelContainer())
{
DocumentModel documentModel = new DocumentModel(document, context);
if (documentModel.Сorpus == null)
throw new Exception("Корпуса c таким id не существует");
context.DocumentModelSet.Add(documentModel);
context.SaveChanges();
document.DocumentId = documentModel.DocumentId;
}
DocumentClient client = GetDocumentClient();
client.CreateDocumentAsync(UriFactory.CreateDocumentCollectionUri(Properties.Resources.DataBaseId, Properties.Resources.DocumentsCollectionId), new AzureDocument(document)).Wait();
Task.Run(() => AddDocumentToIndexStructure(document));
return document.DocumentId;
}
private void AddDocumentToIndexStructure(Document document)
{
string text = document.FlatText;
text = GetTokens(text);
NLAStructureHelper nlaHelper = new NLAStructureHelper(GetDocumentClient(), document.CorpusId);
foreach (string item in text.Split(' ').Distinct())
{
nlaHelper.Add(item, document.DocumentId);
}
}
/// <summary>
/// Очищает от знаков и стоп-слов, стеммит
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
private string GetTokens(string text)
{
string newText = "";
Regex rgx = new Regex("[^a-zA-Zа-яА-Я ]");
text = rgx.Replace(text, "");
string[] words = text.Split(' ');
Stemmer stemmer = new Stemmer();
for (int i = 0; i < words.Length; i++)
{
//убираем стоп-слова
if (words[i].Length != 1 && !stopwords.Contains(words[i]))
{
words[i] = stemmer.stem(words[i]);
newText += " " + words[i];
}
}
return newText.Trim(' ');
}
public int AddUser(User user)
{
if (user == null)
throw new Exception("Параметр не может быть null");
using (var context = new PaperCatModelContainer())
{
UserModel userModel = new UserModel(user);
if (IsThisLoginExist(user.Login, context))
{
throw new Exception("Пользователь с таким логином уже существует");
}
context.UserModelSet.Add(userModel);
context.SaveChanges();
return userModel.UserId;
}
}
private static bool IsThisLoginExist(string sLogin, PaperCatModelContainer context, int userid = 0)
{
var us = context.UserModelSet.Where(u => u.Login == sLogin && u.UserId != userid).FirstOrDefault();
return us != null;
}
public User AutorizeUser(string Login, string Password)
{
using (var context = new PaperCatModelContainer())
{
var userModel = context.UserModelSet.Where(u => u.Login == Login && u.Password == Password).FirstOrDefault();
if (userModel == null) return null; //такого пользователя нет
return userModel.GetUserFromModel();
}
}
public void DeleteCorpus(int corpusId)
{
using (var context = new PaperCatModelContainer())
{
DeleteCorpusInContext(corpusId, context);
context.SaveChanges();
}
}
private void DeleteCorpusInContext(int corpusId, PaperCatModelContainer context)
{
var DocumentList = context.DocumentModelSet.Where(d => d.Сorpus.CorpusId == corpusId).ToList();
DocumentClient client = GetDocumentClient();
foreach (var item in DocumentList)
{
DeleteDocumentInContext(item.DocumentId, context);
Task.Run(() => DeleteDocumentFromIndexStructure(item.DocumentId, corpusId));
string SelfLink = GetDocumentSelfLink(item.DocumentId, client);
if (SelfLink != null)
client.DeleteDocumentAsync(SelfLink).Wait();
}
CorpusModel corpusModel = context.CorpusModelSet.Find(corpusId);
if (corpusModel != null)
{
context.CorpusModelSet.Remove(corpusModel);
}
}
/// <summary>
//// Возвращает id корпуса, из которого был удален документ
/// </summary>
/// <param name="documentId"></param>
/// <param name="context"></param>
/// <returns></returns>
private int DeleteDocumentInContext(int documentId, PaperCatModelContainer context)
{
int corpusId = -1;
DocumentModel documentModel = context.DocumentModelSet.Find(documentId);
if (documentModel != null)
{
corpusId = documentModel.Сorpus.CorpusId;
context.DocumentModelSet.Remove(documentModel);
}
return corpusId;
}
public void DeleteDocument(int documentId)
{
int corpusid;
using (var context = new PaperCatModelContainer())
{
corpusid = DeleteDocumentInContext(documentId, context);
context.SaveChanges();
}
Task.Run(() => DeleteDocumentFromIndexStructure(documentId, corpusid));
DocumentClient client = GetDocumentClient();
string SelfLink = GetDocumentSelfLink(documentId, client);
if (SelfLink != null)
client.DeleteDocumentAsync(SelfLink).Wait();
}
private static string GetDocumentSelfLink(int documentId, DocumentClient client)
{
return client.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(Properties.Resources.DataBaseId, Properties.Resources.DocumentsCollectionId)).AsEnumerable().Where(d => d.GetPropertyValue<int>("DocumentId") == documentId).FirstOrDefault()?.SelfLink;
}
private static DocumentClient GetDocumentClient()
{
return new DocumentClient(new Uri(Properties.Resources.EndpointUrl), Properties.Resources.PrimaryKey);
}
private void DeleteDocumentFromIndexStructure(int documentId, int corpusId)
{
NLAStructureHelper nlaHelper = new NLAStructureHelper(GetDocumentClient(), corpusId, false);
nlaHelper.Delete(documentId);
}
public void DeleteUser(int userId)
{
using (var context = new PaperCatModelContainer())
{
var corpusList = context.CorpusModelSet.Where(c => c.User.UserId == userId).ToList();
foreach (var item in corpusList)
{
DeleteCorpusInContext(item.CorpusId, context);
}
UserModel UserModel = context.UserModelSet.Find(userId);
if (UserModel == null)
return;
context.UserModelSet.Remove(UserModel);
context.SaveChanges();
}
}
public Document[] FindDocuments(string Name, int corpusId)
{
List<DocumentModel> DocumentModelList;
using (var context = new PaperCatModelContainer())
{
DocumentModelList = context.DocumentModelSet.Where(d => d.Сorpus.CorpusId == corpusId && d.Name.ToLower().Contains(Name.ToLower())).ToList();
Document[] documents = new Document[DocumentModelList.Count()];
for (int i = 0; i < DocumentModelList.Count(); i++)
{
documents[i] = GetFullDocument(DocumentModelList[i]);
}
return documents;
}
}
private static Document GetDocumentNonRelationalProperties(Document document, DocumentClient client)
{
Microsoft.Azure.Documents.Document AzureDocument = client.CreateDocumentQuery(UriFactory.CreateDocumentCollectionUri(Properties.Resources.DataBaseId, Properties.Resources.DocumentsCollectionId)).AsEnumerable().Where(d => d.GetPropertyValue<int>("DocumentId") == document.DocumentId).FirstOrDefault();
document.Annotation = AzureDocument.GetPropertyValue<string>("Annotation");
document.FlatText = AzureDocument.GetPropertyValue<string>("FlatText");
document.HTMLstring = AzureDocument.GetPropertyValue<string>("HTMLstring");
return document;
}
public int[] FindTextSimple(string Text, int corpusId)
{
int[] document = null;
var documents = GetDocuments(corpusId);
Text = GetTokens(Text);
foreach (var item in Text.Split(' '))
{
List<int> d = new List<int>();
foreach (var ditem in documents)
{
string str = GetTokens(ditem.FlatText);
if (str.Contains(" " + item + " "))
d.Add(ditem.DocumentId);
}
if (document == null)
{
document = d.ToArray();
}
else
document.Intersect(d.ToArray());
}
return document;
}
public int[] FindText(string Text, int corpusId)
{
//Последовательность вхождения не смотриться, просто наличие ключевых слов в тексте
Text = GetTokens(Text);
int[] documentIds = null;
NLAStructureHelper nlaHelper = new NLAStructureHelper(GetDocumentClient(), corpusId, false);
if (nlaHelper == null)
return null;
foreach (var item in Text.Split(' '))
{
if (documentIds == null)
documentIds = nlaHelper.FindWordDocumentIds(item).ToArray();
else
documentIds.Intersect(nlaHelper.FindWordDocumentIds(item));
}
return documentIds;
//Document[] documents = new Document[documentIds.Count()];