مطالب
اضافه کردن امکان ویرایش WPF DataGrid در صورت نامعتبر بودن سلول ها
اگر با دیتا گرید‌های WPF کار کرده باشید، به این مساله برخورد کرده اید که وقتی روی یک سلول از دیتا گرید Validaion اعمال شده باشد و آن سلول مقدار نامعتبر داشته باشد،امکان ویرایش سایر ردیف‌ها و سلول‌ها وجود ندارد.در بعضی مواقع نیاز است که این رفتار دیتا گرید غیر فعال شود.یکی از راه هایی که می‌توان این کار را انجام داد Override کردن متد OnCanExecuteBeginEdit  مربوط به دیتا گرید و تغییر مقدار CanExecute  در صورت Invalid بودن سلول‌ها است.
protected override void OnCanExecuteBeginEdit(System.Windows.Input.CanExecuteRoutedEventArgs e)
        {
            var hasCellValidationError = false;
            var hasRowValidationError = false;
            const BindingFlags bindingFlags =
                BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Instance;
            //Current cell
            var cellErrorInfo = this.GetType().BaseType.GetProperty("HasCellValidationError", bindingFlags);
            //Grid row
            var rowErrorInfo = this.GetType().BaseType.GetProperty("HasRowValidationError", bindingFlags);
            if (cellErrorInfo != null) hasCellValidationError = (bool) cellErrorInfo.GetValue(this, null);
            if (rowErrorInfo != null) hasRowValidationError = (bool) rowErrorInfo.GetValue(this, null);
            base.OnCanExecuteBeginEdit(e);
            if ((!e.CanExecute && hasCellValidationError) || (!e.CanExecute && hasRowValidationError))
            {
                e.CanExecute = true;
                e.Handled = true;
            }
     }

درحالت پیش فرض هنگامی که CellValidationError یا RowValidationError وجود داشته باشد CanExecute برابر False است و امکان ویرایش دیتا گرید وجود ندارد که در تکه کد بالا این رفتار تغییر داده شده  است.
 
مطالب
چگونگی رسیدگی به Null property در AutoMapper

AutoMapper کتابخانه‌ای برای نگاشت اطلاعات یک شیء به شی‌ءایی دیگر به صورت خودکار می‌باشد.

در این مقاله چگونگی رسیدگی به Null property را در AutoMapper   بررسی خواهیم کرد. فرض کنید شیء منبع دارای یک خاصیت Null  است و می‌خواهید به وسیله Automaper شیء منبع را به مقصد نگاشت نمایید. اما می‌خواهید در صورت Null بودن شیء مبدا، یک مقدار پیش فرض برای شیء مقصد در نظر گرفته شود .

برای نمونه کلاسuser   را که در آن از کلاس Address یک خاصیت تعریف شده، در نظر بگیرید. اگر مقدار آدرس در شیء منبع خالی بود شاید شما بخواهید مقدار آن را به صورت empty string و یا با یک مقدار پیش فرض در مقصد مقدار دهی کنید.

همانند مثال زیر : 

public class UserSource
{
  public Address Address{get;set;}
}
 
public class UserDestination
{
  public string Address{get;set;}
}
ابتدا نگاشت‌ها را تعریف می‌کنیم:
AutoMapper.Mapper.CreateMap<UserSource, UserDestination>()
          .ForMember(dest => dest.Address
          , opt => opt.NullSubstitute("Address not found")
          );
کد بالا نشان دهنده تبدیل Address به Address است ولی دارای متد اختیاری NullSubstitute می‌باشد و بیانگر این است که اگر آدرس شیء منبع Null بود، مقدار پیش فرضی را برای شیء مقصد در نظر بگیرد. در انتها  می‌توان نگاشت را در برنامه متناسب با نیاز خود انجام داد:
var model = AutoMapper.Mapper.Map<UserSource, UserDestination>(user);
var models = AutoMapper.Mapper.Map<IEnumerable<UserSource>, IEnumerable<UserDestination>>(users);
پاسخ به بازخورد‌های پروژه‌ها
درخواست راهنمایی سیستم Decision
من متد LoadEntiies رو بهصورت زیر تغییر دادم مشکل حل شد
  private static void LoadEntities(Assembly asm, DbModelBuilder modelBuilder, string nameSpace)
        {
            var entityTypes = asm.GetTypes()
                .Where(type => type.BaseType != null &&
                               type.BaseType != Type.GetType("System.Enum") &&
                               type.Name != "Entity" &&
                               type.Name != "BaseEntity" &&
                               type.Namespace != null &&
                               type.Namespace.Contains(nameSpace))
                .ToList();

            entityTypes.ForEach(modelBuilder.RegisterEntityType);
        }
از نظر من کد قبلی چندتا مشکل داشت
1- توی قسمت where  یک بار  baseType رو مخالف null و یک بار هم برابر null خواسته بود که این خودش باعث میشد تا کوئری گرفته شده  مقداری رو برنگردونه
2- شرطی که نوشته شده بود اگه مشکل بالا رو حل میشد باعث می‌شد تمام Class‌ها و Enumeration‌ها و Object هایی که جزء entity نبود رو باخودش بیاره و به عنوان یک Entity معرفی کنه
3- اینکه توی شرط نوشته شده بود Namespace==nameSpace و این یعنی اگر فضای نام کلاسی مثلا برابر DomainClasses.Entities.Common  بود، توی لیست entity‌های آورده نشه
خلاصه اینکه کوئری قبلی هیچ چیزی رو برنمیگردوند
من با کد بالایی مشکلم رو حل کردم ولی فکرکنم کدی دوستان در پست‌های قبلی گذاشتن بهتر باشه  
پاسخ به بازخورد‌های پروژه‌ها
مشکل با نوشتن تابع تجمعی سفارشی(از طریق پیاده سازی IAggregateFunction)
باسلام
فکر میکنم یه مشکل کوچیکی وجود داره.
من از این کد در رویداد CellCreated استفاده کردم.
if (args.CellType == CellType.PreviousPageSummaryCell ||
                        args.CellType == CellType.PageSummaryCell ||
                        args.CellType == CellType.SummaryRowCell)
                    {
                        if (args.TableRowData != null && args.Cell.RowData.PropertyName == "CaclulatedDetection")
                        {
                            var summaryRowCredit = args.TableRowData.FirstOrDefault(x => x.PropertyName == "Creditor");
                            var summaryRowDebit = args.TableRowData.FirstOrDefault(x => x.PropertyName == "Debtor");

                            if (summaryRowCredit != null && summaryRowDebit != null)
                            {
                                args.Cell.RowData.FormattedValue =
                                    (summaryRowCredit.PropertyValue.ToSafeDouble() - summaryRowDebit.PropertyValue.ToSafeDouble()) > 0 ? "بستانکار" : "بدهکار";
                            }
                        }

                    }
      ولی چون ترتیب Render شدن سلول‌ها به ترتیب از سمت چپ به راست است (یعنی به عنوان مثال در گزارش من به ترتیب سلول مانده، سپس تشخیص، بدهکار و بستانکار Initialize می‌شوند)، لذا هنگامی که سلول مربوط به ستون "تشخیص" در سطرهای جمع کل و جمع کل صفحه Render می‌شوند مقدار  summaryRowCredit و   summaryRowDebit نال(Null) می‌باشد.
فکر میکنم در مثالی که شما زحمت کشیدید (AccountingBalanceColumnPdfReport) چون 
 doc.RunDirection(PdfRunDirection.LeftToRight);
در گزارش خودم نیز هنگامی که LeftToRight تنظیم نمودم، درست شد ولی خوب برعکس چاپ میشه دیگه.

در واقع فکر میکنم وقتی گزارش رو به صورت RightToLeft هم تنظیم می‌کنیم بازم از سمت چپ شروع میکنه به render کردن سلول‌های یک سطر.

پاسخ به بازخورد‌های پروژه‌ها
چگونگی قرار دادن عکس به صورت Watermark(پس زمینه گزارش)
یک پارامتر اختیاری در اینجا اضافه کردم:
public void BackgroundImage(string backgroundImageFilePath, System.Drawing.PointF? position = null)
با استفاده از پارامتر position می‌تونید X و Y دلخواه خودتون رو اعمال کنید.
اگر نال باشد (حالت پیش فرض)، وسط صفحه به صورت خودکار محاسبه خواهد شد.
این تغییرات فعلا در SVN است.
پاسخ به بازخورد‌های پروژه‌ها
چگونگی قرار دادن عکس به صورت Watermark(پس زمینه گزارش)
در قسمت DocumentPreferences از متد BackgroundImage استفاده کنید.
البته باید دقت داشته باشید که اگر تصویر زمینه، پس از تنظیم ذکر شده مشاهده نشد، نیاز است رنگ سلول‌های گرید رو شفاف کنید. یعنی باید یک قالب سفارشی طراحی کنید که رنگ‌ها رو null برگردونه تا transparency لازم جهت نمایش تصویر قرار گرفته در زیر جدول مهیا شود.
بازخوردهای پروژه‌ها
خطا در زمان اجرا
سلام
با تشکر فراوان بابت پروژه
نمی دونم در حین اجرا چرا این خطا رو اعلام می‌کنه
JavaScript runtime error: Unable to get property 'animatedLayers' of undefined or null reference 

مطالب
استخراج تمام XPathهای یک محتوای HTMLایی به کمک کتابخانه HtmlAgilityPack
اولین قدم کار کردن با کتابخانه قدرتمند HtmlAgilityPack، داشتن XPath معتبر و متناظر با یک گره خاص می‌باشد. هرچند به ظاهر تعدادی از مرورگرها با کمک افزونه‌های خود امکان استخراج این XPathها را فراهم کرده‌اند اما ... عموما این مقادیر ارائه شده، نادرست هستند و بر روی محتوای HTML اصلی یک سایت قابل اجرا نیستند؛ علت هم به نرمال سازی‌های انجام شده بر روی محتوای یک سایت، توسط موتور مرورگر بر می‌گردد.
خود کتابخانه HtmlAgilityPack به ازای هر HtmlNode ایی که ارائه می‌دهد، خاصیت XPath معتبری را نیز به همراه دارد. در ادامه قصد داریم از این امکان توکار استفاده کرده و کلیه XPath‌های یک محتوای HTML ایی را استخراج کنیم.


پردازش تگ‌های تو در توی یک HTML به کمک کتابخانه HtmlAgilityPack

using System;
using System.Linq;
using System.Net;
using System.Text;
using HtmlAgilityPack;

namespace HapTests
{
    public class HtmlReader
    {
        public Action<string> ParseError { set; get; }

        public Func<HtmlNode, bool> ParserHtmlNode { set; get; }

        public void StartParsingHtml(Uri url)
        {
            using (var client = new WebClient { Encoding = Encoding.UTF8 })
            {
                client.Headers.Add("user-agent", "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)");
                StartParsingHtml(client.DownloadString(url));
            }
        }

        public void StartParsingHtml(string htmlContent)
        {
            if (string.IsNullOrWhiteSpace(htmlContent))
                throw new ArgumentNullException("content");

            var doc = new HtmlDocument
            {
                OptionCheckSyntax = true,
                OptionFixNestedTags = true,
                OptionAutoCloseOnEnd = true,
                OptionDefaultStreamEncoding = Encoding.UTF8
            };
            doc.LoadHtml(htmlContent);

            if (doc.ParseErrors != null && doc.ParseErrors.Any())
            {
                foreach (var error in doc.ParseErrors)
                {
                    if (ParseError != null)
                        ParseError(error.Code + " - " + error.Reason);
                }
            }

            if (!doc.DocumentNode.HasChildNodes)
                return;

            handleChildren(doc.DocumentNode.ChildNodes);
        }

        private void handleChildren(HtmlNodeCollection nodes)
        {
            foreach (var itm in nodes)
            {
                if (itm.Name.ToLower().Equals("html"))
                {
                    if (itm.Element("body") != null)
                        handleChildren(itm.Element("body").ChildNodes);
                }
                else
                    handleHtmlNode(itm);
            }
        }

        private void parserChildNodes(HtmlNode content)
        {
            foreach (var item in content.ChildNodes)
            {
                handleHtmlNode(item);
            }
        }

        private void handleHtmlNode(HtmlNode htmNode)
        {
            switch (htmNode.Name.ToLower())
            {
                case "html":
                case "body":
                    handleChildren(htmNode.ChildNodes);
                    break;

                default:
                    if (ParserHtmlNode == null)
                        throw new ArgumentNullException("ParserHtmlNode");

                    if (ParserHtmlNode(htmNode))
                        parserChildNodes(htmNode);

                    break;
            }
        }
    }
}
در اینجا کدهایی را ملاحظه می‌کنید که علاوه بر ارائه تنظیمات اولیه HtmlAgilityPack (خصوصا با درنظر گرفتن مباحث ورودی یونیکد)، به صورت بازگشتی (با توجه به اینکه الزاما مسیر یا Node خاصی مدنظر نیست)، کلیه گره‌های یک HTML را بررسی و ارائه می‌دهند.
این کد برای نوشتن مبدل‌های HTML به XYZ بسیار مناسب است. برای مثال اگر بخواهید یک مبدل HTML به PDF را تهیه کنید، کدهای ابتدایی آن همین موارد است:
new HtmlReader
{
    ParseError = error => Console.WriteLine(error),
    ParserHtmlNode = htmlNode =>
    {
        //switch(htmlNode.Name) { }
        return true; //it's a nested node.
    }
}.StartParsingHtml(html);
نمونه‌ای از نحوه استفاده از کدهای کلاس HtmlReader را ملاحظه می‌کنید.
در اینجا html، محتوای HTMLایی در حال بررسی است. ParserHtmlNode یک callback است. هر زمانیکه به یک گره HTML برخورد، آن‌را در اختیار شما قرار می‌دهد. در ادامه فرصت خواهید داشت تا برای نمونه یک swicth را تهیه کرده و مثلا به ازای تگ hr یک خط رسم کنید، به ازای تگ br یک سطر جدید را درنظر بگیرید و الی آخر. اگر خروجی این Func را true درنظر بگیرید، فرض بر این خواهد بود که گره جاری تو در تو است (حالت دنیای واقعی)؛ در غیراینصورت، یک سطح این گره، بیشتر بررسی نخواهد شد.
در این کلاس، ParseError نیز یک callback است و اگر کتابخانه HtmlAgilityPack، در حین آنالیز کدهای HTML دریافتی به خطایی برخورد، آن‌را گزارش خواهد داد.
در کلاس فوق، دو حالت برای متد StartParsingHtml در نظر گرفته شده است. در حالت اول، یک Uri یا آدرس اینترنتی دریافت و سپس آنالیز می‌گردد. در حالت دوم، فرض بر این است که محتوای کدهای HTML مدنظر به هر نحوی پیشتر تهیه شده و به صورت string موجود است.


استخراج کلیه XPathها از یک فایل HTML به کمک کتابخانه HtmlAgilityPack

اکنون که یک HTML Parser عمومی را تهیه کرده‌ایم، استخراج XPathها توسط آن کار ساده‌ای خواهد بود. یک مثال کامل را در این زمینه در ادامه ملاحظه می‌کنید:
using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using HtmlAgilityPack;

namespace HapTests
{
    class Program
    {
        static void Main(string[] args)
        {
            var html = 
                @"<table width='750' border='0' style='font-size: 10pt; width: 736px' class='boxcar2 gerd'>
            <tbody><tr>
            <td height='70' colspan='4' class='boxcart1 gerd'>
            <iframe width='718' scrolling='no'>
            </iframe></td>
            </tr>
            <tr>
            <td height='70' colspan='4' class='boxcart1 gerd'>
    </td>
            </tr>
            <tr>
            <td width='193' height='36' class='boxcart2 gerd'>
            <a target='_self' href='Curr.cbi.2.php'>نرخ ارز مبادله ای بانک مرکزی</a></td>
            <td width='181' height='36' class='boxcart2 gerd'>
            <a target='_self' href='Curr.cbi.php'>نرخ ارز مرجع بانک مرکزی</a></td>
            <td width='149' height='36' class='boxcart2 gerd'>
            <a target='_self' href='curv.htm'>نمودار قیمت طلا</a></td>
            <td width='199' height='36' class='boxcart2 gerd'>
            <a target='_self' href='index.php'>قیمت طلا و سکه در بازار ایران</a></td>
            </tr>
            <tr>
            <td height='48' colspan='4' class='boxcart1 gerd'>
            <p dir='rtl'><span style='font-size: 13pt;'>تابلو آنلاین قیمت جهانی طلا و نقره ( دلار 
            )</span></p></td>
            </tr>
            <tr>
            <td height='57' colspan='2' class='boxcart1 gerd'>قیمت لحظه ای هر انس 
            نقره در بازارهای جهانی<br>
            <span style='font-size: 9pt;'>
            </span></td>
            <td height='57' colspan='2' class='boxcart1 gerd'>قیمت لحظه ای هر انس 
            طلا در بازارهای جهانی<br>
            <span style='font-size: 9pt;'>
            </span></td>
            </tr>
            <tr>
            <td height='48' colspan='4' class='boxcart1 gerd'>
            <p dir='rtl'><span style='font-size: 13pt'>تابلو آنلاین قیمت طلا ، سکه 
            و نقره در بازار ایران ( ریال )</span></p>
            </td>
            </tr>
            <tr>
            <td style='direction: rtl; font-size: 8pt' colspan='4'><div align='center'>
                            <table id='gold_tbl'><tbody><tr><th>قیمت طلا</th><th>قیمت زنده</th><th>تغییر</th>
                            <th>کمترین</th><th>بیشترین</th><th>زمان</th></tr><tr><td>انس طلا <sup>دلار</sup></td>
                            <td class='s0_1'>1,375.90</td><td class='c0_1 neg'>(-0.34%) -4.70</td>
                            <td class='l0_1'>1,374.90</td><td class='h0_1'>1,380.80</td><td class='z0_1 fa'>17:53</td>
                            </tr><tr><td>مثقال طلا</td><td class='s3_2'>5,290,000</td>
                            <td class='c3_2 pos'>(1.63%) 85,000</td><td class='l3_2'>5,200,000</td><td class='h3_2'>5,320,000</td><td class='z3_2 fa'>17:50</td></tr><tr><td>گرم طلای 18</td>
                            <td class='s3_3'>1,221,200</td><td class='c3_3 pos'>(1.63%) 19,600</td><td class='l3_3'>1,200,400</td><td class='h3_3'>1,228,100</td><td class='z3_3 fa'>17:50</td>
                            </tr><tr><td>انس نقره <sup>دلار</sup></td><td class='s0_5'>21.83</td><td class='c0_5'>(0.00%) 0.00</td><td class='l0_5'>21.67</td><td class='h0_5'>21.96</td>
                            <td class='z0_5 fa'>17:53</td></tr></tbody></table><br><table id='coin_tbl'><tbody><tr><th>سکه</th><th>قیمت زنده</th><th>تغییر</th><th>کمترین</th>
                            <th>بیشترین</th><th>ارزش طلا</th><th>زمان</th></tr><tr><td>بهار آزادی</td><td class='s3_10'>12,650,000</td><td class='c3_10 pos'>(2.68%) 330,000</td>
                            <td class='l3_10'>12,320,000</td><td class='h3_10'>12,650,000</td><td class='z4_10'>11,918,400</td><td class='z3_10 fa'>16:07</td></tr><tr><td>امامی</td>
                            <td class='s3_11'>12,960,000</td><td class='c3_11 pos'>(2.61%) 330,000</td><td class='l3_11'>12,630,000</td><td class='h3_11'>13,050,000</td><td class='z4_11'>11,918,400</td>
                            <td class='z3_11 fa'>17:43</td></tr><tr><td>نیم</td><td class='s3_12'>6,880,000</td><td class='c3_12 pos'>(2.69%) 180,000</td><td class='l3_12'>6,700,000</td>
                            <td class='h3_12'>6,900,000</td><td class='z4_12'>5,959,200</td><td class='z3_12 fa'>16:08</td></tr><tr><td>ربع</td><td class='s3_13'>4,250,000</td><td class='c3_13 pos'>(2.41%) 100,000</td>
                            <td class='l3_13'>4,150,000</td><td class='h3_13'>4,300,000</td><td class='z4_13'>2,978,100</td><td class='z3_13 fa'>17:42</td></tr><tr><td>گرمی</td><td class='s3_14'>2,940,000</td>   
                            <td class='c3_14 pos'>(3.16%) 90,000</td><td class='l3_14'>2,850,000</td><td class='h3_14'>2,940,000</td><td class='z4_14'>1,465,400</td><td class='z3_14 fa'>17:40</td></tr></tbody></table></div></td>
            </tr>
            </tbody></table>
                ";

            extractXPath(html);
            test(html);
        }

        /// <summary>
        /// Converts /#comment[1] to /comment()[1] 
        /// or /#text[1] to /text()[1]
        /// </summary>
        private static string GetValidXPath(string xpath)
        {
            var index = xpath.LastIndexOf("/");
            var lastPath = xpath.Substring(index);

            if (lastPath.Contains("#"))
            {
                xpath = xpath.Substring(0, index);
                lastPath = lastPath.Replace("#", "");
                lastPath = lastPath.Replace("[", "()[");
                xpath = xpath + lastPath;
            }

            return xpath;
        }

        private static void extractXPath(string html)
        {
            var sb = new StringBuilder();
            new HtmlReader
            {
                ParseError = error => Console.WriteLine(error),
                ParserHtmlNode = htmlNode =>
                {
                    if (htmlNode is HtmlTextNode)
                    {
                        sb.AppendLine("Text NodeName: " + htmlNode.Name.Trim());
                        sb.AppendLine("InnerText: " + htmlNode.InnerText.Trim());
                    }
                    else
                    {
                        sb.AppendLine("NodeName: " + htmlNode.Name.Trim());
                        var nodeText = new StringBuilder();
                        for (int i = 0; (i < htmlNode.OuterHtml.Length && htmlNode.OuterHtml[i] != '>'); i++)
                            nodeText.Append(htmlNode.OuterHtml[i]);

                        nodeText.Append(">");

                        sb.AppendLine("Node Start: " + nodeText.ToString());
                    }

                    sb.AppendLine("XPath: " + GetValidXPath(htmlNode.XPath.Trim()));
                    sb.AppendLine(Environment.NewLine);

                    return true; //it's a nested node.
                }
            }.StartParsingHtml(html);

            File.WriteAllText("xpath.txt", sb.ToString());
            Process.Start("xpath.txt");
        }

        private static void test(string html)
        {
            var doc = new HtmlDocument
            {
                OptionCheckSyntax = true,
                OptionFixNestedTags = true,
                OptionAutoCloseOnEnd = true,
                OptionDefaultStreamEncoding = Encoding.UTF8
            };
            doc.LoadHtml(html);
            var node = doc.DocumentNode.SelectSingleNode("/table[1]/tbody[1]/tr[7]/td[1]/div[1]/table[2]/tbody[1]/tr[6]/td[7]/text()[1]");
            Console.WriteLine(node.InnerText);
        }
    }
}
در این مثال html مقداری است که از یک سایت عمومی دریافت شده است.
سپس نمونه‌ای دیگر از نحوه استفاده از کلاس HtmlReader قسمت قبل را در ادامه، در متد extractXPath ملاحظه می‌کنید. در اینجا کلاس HtmlReader در یک عملیات بازگشتی، کلیه گره‌های تو در توی HTML مورد نظر را آنالیز کرده و توسط callback ایی به نام ParserHtmlNode در اختیار ما قرار می‌دهد. اکنون که این htmlNode را داریم، خاصیت XPath آن دقیقا مقداری است که به دنبالش هستیم.
در اینجا چند نکته حائز اهمیت هستند:
- با بررسی HtmlTextNode، به نودهایی خواهیم رسید که دارای مقدار متنی هستند. در غیراینصورت این گره، خود ابتدای یک سری گره تو در توی دیگر است.
- XPath بازگشتی توسط کتابخانه HtmlAgilityPack نیاز به کمی تمیز سازی دارد. اینکار در متد GetValidXPath انجام شده است.
- در متد test انتهایی، نمونه‌ای از نحوه استفاده از XPathهای استخراجی را ملاحظه می‌کنید.
Text NodeName: #text
InnerText: 17:40
XPath: /table[1]/tbody[1]/tr[7]/td[1]/div[1]/table[2]/tbody[1]/tr[6]/td[7]/text()[1]
برای نمونه سه سطر فوق، یکی از مداخل فایل نهایی تولیدی مثال جاری است. اکنون که XPath را داریم، استفاده از آن جهت استخراج مقدار InnerText مدنظر، ساده خواهد بود.
مطالب
دسته بندی سطر های جدول
فرض کنید میخواهیم سطرهای جدول را 6 تا 6 تا سوا کنیم و به هر کدام یک عددی انتساب دهیم و هر قسم تولید شده را نیز 2 تا 2 تا سوا کنیم و بهش عدد انتساب دهیم.
به تصویر زیر توجه بفرمایید. ابتدا داده‌ها به دو دسته ششتایی تقسیم شدن(ستون ntl)، سپس هر کدام از این دسته‌ها نیز به سه دسته دوتایی تقسیم شدن(ستون  grp) هدف ما تولید دو ستون ntl و grp توسط query می‌باشد.


برای بدست آوردن مقادیر دو ستون مذکور روش‌های متنوعی وجود دارد که برخی از آنها را در انیجا پوشش میدم.
قبل از هر چیزی ابتدا جدول را ایجاد و 12 سطر زیر را در آن انتشار دهید:
CREATE TABLE T (nbr INT NOT NULL);
INSERT T VALUES (1), (2), (3), (4), (5), (6), 
                (7), (8), (9), (10), (11), (12);

روش اول:
این روش، تعمیم پذیری و پویایی ندارد و برای هر سناریویی مناسب نخواهد بود. ولی از آنجایی که دیدم کوئری زیر میتواند یک نمونه از کاربرد Ntile باشه آن را مطرح کردم.
SELECT nbr, ntl, NTILE(3) OVER(PARTITION BY ntl ORDER BY nbr) AS grp
FROM (
         SELECT nbr, NTILE(2) OVER(ORDER BY nbr) ntl
         FROM T
     ) AS D;
تابع ntile داخلی سطرهای جدول را به دو قسم تقسیم می‌کند و برای قسم اول عدد 1 و برای قسم دوم عدد 2 را در نظر می‌گیرد.
تابع ntile بیرونی بر اساس دو عدد 1و 2 گروه بندی انجام داده و هر گروه را به 3 قسمت تقسیم می‌کند. قسمت اول 1، دوم 2 و سوم 3 خواهد بود.
لازم به ذکر است که باید خارج قسمت تقسیم تعدادسطرها بر عدد ntile یک عدد صحیح باشد تا خروجی مناسب داشته باشیم. و همچنین بایستی بدانیم که تعداد سطرهای جدول چنتاست تا آن را به گونه ای تقسیم کنیم که خارج قسمت برابر شود با عدد مورد نظر ما یعنی 6.

روش دوم:
در این روش بر خلاف روش قبل که همه چیز توسط تابع بدست می‌آمد باید خودمان دست به کار شویم و فرمولی را بدست آوریم که نتیجه مورد نظر را تولید کند.
برای حل این مساله ابتدا باید سطرهای جدول را 6 تا 6 تا سوا کنیم و عناصر هر دسته را شماره گذاری کنیم (از 1 تا 6 بر اساس ترتیب مقدار nbr) سپس با کمک سایر فرمول‌ها دسته‌ها را دوتا دوتا شماره گذاری میکنیم.

به تصویر زیر توجه بفرمایید:

در کادر نارنجی رنگ همانطور که اشاره شد ما سطرهای شمارگذاری شده ای داریم که در رنج 1 تا 6 هستند. و در کادر بنفش ستون مورد نظر ما قرار دارد. ستون بنفش با کمک ستون نارنجی بدست آمده است.
اگر تقسیم صحیح را div و باقیمانده صحیح را mod بگیریم فرول‌های مورد نظر به این شرح خواهد بود:

طبق کوئری زیر ستون نارنجی(rnk1/rnk2) را به دو طریق می‌توان ایجاد نمود و ستون بنفش(grp1/grp2) را نیز به دو طریق میتوان ایجاد نمود.
SELECT nbr, rnk1, rnk2,
       (rnk1 + 1) / 2 AS grp2,
       (rnk1 - 1) / 2 + 1 AS grp2
FROM
(
SELECT nbr, 
       ROW_NUMBER() OVER(PARTITION BY (nbr + 5) / 6 ORDER BY nbr) rnk1,
       (nbr - 1) % 6 + 1 AS rnk2
FROM t
)d

مطالب
آیا بومی‌سازی همه چیز ضرورت دارد؟
چنانکه در مقاله قبلی هم گفتم بومی‌سازی صرفا در ترجمه خلاصه نمی‌شود و یک فرآیند است. امروز یک مثال کوچک از روند بومی‌سازی در ارائه ایده‌ها به سرمایه‌گذاران را با هم بررسی خواهیم کرد.

در کشورهایی که فرهنگ راه‌اندازی استارتاپ‌ها جا افتاده است، سرمایه‌گذاران در جستجوی ایده‌های ناب و تیم‌های موفق هستند تا با سرمایه‌گذاری بر روی آنها در اندک زمانی سرمایه‌شان را چند برابر کنند. اما معمولا روند این کار برعکس است، به این معنی که معمولا شخصی که ایده دارد در جستجوی سرمایه‌گذار به دفتر او (که معمولا در طبقات بالایی برج‌های بلند هستند) رفته و سعی در ارائه ایده‌اش به سرمایه‌گذار می‌کند. مشخصا پیدا کردن و گرفتن زمان از یک سرمایه‌گذار بسیار دشوار است، لذا تنها فرصتی که صاحبان ایده دارند زمانی است که سرمایه‌گذار وارد ساختمان شده، از آسانسور بالا رفته و وارد دفترش می‌شود. این زمان فرصتی کوتاه است که صاحب ایده، خودش، ایده‌اش، نقاط قوت ایده‌اش، طرح تجاری و دلایل سودآوری آن را به سرمایه‌گذار توضیح دهد. و همه این‌ها بصورت میانگین مفهومی به عنوان ارائه‌های آسانسوری (Elevator Presentation / Pitch) را برای همه نهادینه کرده. نهایتا در انتهای این یک دقیقه در صورتی که سرمایه‌گذار طرح را پذیرفت برای توضیحات بیشتر یک وقت دیگر برای توضیحات تکمیلی به صاحب ایده می‌دهد که این وقت مجدد معمولا فقط پنج دقیقه است. نهایتا تعداد این جلسات و زمان آنها بیشتر شده تا سرمایه‌گذار بپذیرد که روی ایده سرمایه‌گذاری کند.



اما هیچ‌کدام از اتفاقات بالا، در ایران قابل لمس نیست چراکه ما هنوز در ابتدای راه هستیم و مفهوم استارتاپ‌ها تازه در حال شکل‌گیری است، سرمایه‌گذاران هنوز بطور جدی به این طرح‌ها نگاه نمی‌کنند. اما اگر بخواهیم همین روند بالا را بومی کنیم. باید به این موارد دقت کنیم.

  • زمان لازم برای ارائه اولیه (ارائه آسانسوری) در ایران چقدر باید باشد؟
  • زمان ارائه دوم چقدر باید باشد؟
  • چه نکاتی در ارائه اول و دوم باید گنجانده شود؟
برای پاسخگویی به این پرسش‌ها باید به این موارد توجه کرد.

دفتر سرمایه‌گذاران ما معمولا در طبقات بالایی یک برج نیست. سرمایه‌گذاران ما معمولا یک ساختمان جداگانه برای خودشان دارند که در آن مستقر هستند حالا ممکن است این ساختمان چند  طبقه باشد اما کل این مکان متعلق به شرکتی است که او اداره می‌کند و او در یک طبقه خاص مستقر است. فرهنگ ما به ما اجازه نمی‌دهد تا بدون وقت قبلی و با سرمایه‌گذار (که در اکثر مواقع شخص اصلی شرکت است) وارد شرکت شویم. وقتهای ارائه معمولا بصورت رابطه‌ای و در صورتی که شخصی یک صاحب ایده را به سرمایه‌گذار معرفی کرده باشد تعیین خواهند شد. که متاسفانه زمان این جلسات به نسبت جدی بودن سرمایه‌گذار در سرمایه‌گذاری و توسط خود او تعیین می‌شود و شخص ارائه دهنده از قبل هیچ اطلاعی از اینکه چه زمانی را برای ارائه ایده‌اش در اختیار دارد، ندارد.


لذا با توجه به این موارد، ما در ایران جلسه اول و دوم را، معمولا یکی می‌کنیم. و متاسفانه بجای تاکید بر روی ایده و استارتاپ، در جلسه اول معمولا بیشتر بر روی خود روابط تاکید می‌شود تا یک اطمینان در هر دو طرف ایجاد شود. صاحبان ایده همیشه از این می‌ترسند که سرمایه‌گذار پس از شنیدن ایده بدون خود آنها اقدام به پیاده‌سازی آن کند، لذا در جلسات اول صرفا هر دو طرف اقدام به براندازی همدیگر کرده و با جملات نامفهوم از ایده اصلی سعی در به تعویق انداختن مباحث فنی کرده و صرفا سعی کنند از طرف مقابل مطمئن شوند.

اما آیا واقعا ما به ارائه‌های آسانسوری نیازی نداریم؟ جواب این سوال منفی است. چراکه گرچه با توجه به ساختار سرمایه‌گذاری در کشور ما نمی‌توانیم اقدام به برپایی ارائه آسانسوری با یک سرمایه‌گذار کنیم، اما اگر نتوانیم تمام مواردی که در مفهوم ارائه اسانسوری نهفته است را، در یک دقیقه به هر شخصی ارائه کنیم نشان‌دهنده این است که طرح ما واقعا برای سرمایه‌گذاری مناسب نیست. بهتر است برای خودتان و دوستانتان و اعضای تیمتان اقدام به برپایی این ارائه‌ها کنید تا نقاط مبهم برای خود شما مشخص شود. شاید واقعا باید بپذیرید که استارتاپ شما محکوم به شکست است.

نکته دیگری که در بومی‌سازی باید توجه داشت دقیقا همین مورد است. که همه چیز را نباید و نمی‌توان بومی کرد. ساختارهای کشور ما و آنچه ما با آنها درگیر هستیم تعیین کننده آن است که ایا باید چیزی را بومی کرد یا خیر. نظر شخص من این است که با توجه به فرهنگ کاریمان در ایران، در واقعیت، ما هیچگاه نخواهیم توانست به سمت ارائه‌های آسانسوری واقعی حرکت کنیم. لذا باید آنها را برای خودمان تمرین کنیم تا خودمان از شکست یا احتما پیروزی طرحمان اطمینان کسب کنیم.

نظر شما چیست؟